REVO8100xCalendar Events"P-- Calendar Events produces a web site calendar that is: -- "dynamically created" (the html is written on the fly by the server), -- "responsive" (adapts its size and presentation to fit the device window) -- and has a corresponding app (this app) for creating the files -- used to populate the web calendar with events. -- MIT Licensed ------------------------------------------------------------------------------ -- Calendar Events -- Copyright (c) 2021 91ֱ <> -- Permission is hereby granted, free of charge, to any person -- obtaining a copy of this software and associated documentation file -- (the "91ֱ"), to deal in the 91ֱ without restriction, -- including without limitation the rights to use, copy, modify, merge, -- publish, distribute, sublicense, and/or sell copies of the 91ֱ, -- and to permit persons to whom the 91ֱ is furnished to do so, -- subject to the following conditions: -- The above copyright notice and this permission notice -- shall be included in all copies or substantial portions of the 91ֱ. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -- OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ local aUndoEvent -- an array holding the raw contents of all the fields (and button) that the user fills out for an event local aRedoEvent -- an array holding the raw contents of all the fields (and button) that the user fills out for an event... saved just before an Undo global gRevAppIcon global gRevSmallAppIcon constant kMain = "Calendar Events" on openStack start using stack "elsoStandardLib" put 11561 into gRevAppIcon put 11562 into gRevSmallAppIcon NewDocument PreferenceRead -- developer tool only hide fld "devNotes" -- init if the environment is "development" then set the editMenus of this stack to "true" show btn "strip-n-ship" show btn "recoverPreferences" show btn "devNotes" else set the editMenus of this stack to "false" hide btn "strip-n-ship" hide btn "recoverPreferences" hide btn "devNotes" end if put the text of btn "File" into tFileMenu if IsWindows() then put "&Exit" into line -1 of tFileMenu put "false" into tSpecialCase else -- MacOS put "&Quit/Q" into line -1 of tFileMenu put "true" into tSpecialCase end if set the text of btn "File" to tFileMenu repeat with x = 1 to the number of buttons in grp "numberPadGrp" set the threeD of btn x of grp "numberPadGrp" to tSpecialCase end repeat repeat with x = 1 to the number of buttons in grp "repeatingDayTypeGrp" set the threeD of btn x of grp "repeatingDayTypeGrp" to tSpecialCase end repeat end openStack on closeStack set the uUndoEvent of stack kMain to empty set the dgText of group "EventDataGrid" to empty if the environment is "standalone" then PreferenceWrite Quit end if end closeStack --|------------------------------------------------------ --| This does not actually create a new document on disk --| It preps all the app's entry spaces for a clean start --|------------------------------------------------------ command NewDocument pYear set the uEventLineToEdit of stack kMain to empty -- init set the uEventIndexNumToEdit of stack "Calendar Events" to empty -- init set the dgHilitedLines of group "EventDataGrid" to empty -- init set the label of btn "submit" to "Submit New Event" ClearItOut put the dgText of group "EventDataGrid" into tUndoText -- just in case if (tUndoText is not empty) AND (the short name of the target is not "strip-n-ship") then set the uUndoEvent of stack "Calendar Events" to tUndoText UndoMenuNameChange "New File" --set the label of btn "delete" to "Undo" --set the textAlign of btn "delete" to "center" --set the icon of btn "delete" to 1032 --enable btn "delete" --set the blendlevel of btn "delete" to 0 else set the uUndoEvent of stack "Calendar Events" to empty disable menuItem 1 of btn "Edit" --set the label of btn "delete" to " Delete Event" --set the textAlign of btn "delete" to "right" --set the icon of btn "delete" to 1031 --disable btn "delete" --set the blendlevel of btn "delete" to 50 end if set the dgText of group "EventDataGrid" to empty set the uFileState of stack kMain to empty set the uRemoteSourceFile of stack kMain to "false" set the icon of btn "fileStateIndicator" to 4117 -- gray (no file) set the label of btn "submit" to "Submit New Event" set the icon of btn "writeFile" to 1025 -- upload to cloud (5257= cloud-check) put pYear into fld "eventListYear" --if pYear is empty then disable group "EventDataGrid" --end if DeleteEventMenuItemEnableDisable "disable" end NewDocument --|------------------------------------------------------ --| Allows the Undo of event data to be reversed --| uses the script local aRedoEvent[] --|------------------------------------------------------ command RedoNewEvent lock screen -- put the aRedoEvent text back put aRedoEvent["eventTitle"] into fld "eventTitle" put aRedoEvent["eventDate"] into fld "eventDate" put aRedoEvent["eventTime"] into fld "eventTime" put aRedoEvent["eventTimeStart"] into fld "eventTimeStart" put aRedoEvent["eventTimeFinish"] into fld "eventTimeFinish" put aRedoEvent["eventDetails"] into fld "eventDetails" put aRedoEvent["startDate"] into fld "startDate" put aRedoEvent["endDate"] into fld "endDate" put aRedoEvent["days"] into fld "days" put aRedoEvent["addException"] into fld "addException" set the label of btn "exceptionMenu" to aRedoEvent["exceptionMenuLabel"] set the text of btn "exceptionMenu" to aRedoEvent["exceptionMenuText"] unlock screen end RedoNewEvent --|------------------------------------------------------ --| If the "New Event" btn is clicked instead of the "Submit New Event" btn --| this provides a way of getting the lost information back --| uses the script local aRedoEvent[] --|------------------------------------------------------ command UndoNewEvent lock screen -- record the redo information put fld "eventTitle" into aRedoEvent["eventTitle"] put fld "eventDate" into aRedoEvent["eventDate"] put fld "eventTime" into aRedoEvent["eventTime"] put fld "eventTimeStart" into aRedoEvent["eventTimeStart"] put fld "eventTimeFinish" into aRedoEvent["eventTimeFinish"] put fld "eventDetails" into aRedoEvent["eventDetails"] put fld "startDate" into aRedoEvent["startDate"] put fld "endDate" into aRedoEvent["endDate"] put fld "days" into aRedoEvent["days"] put fld "addException" into aRedoEvent["addException"] put the label of btn "exceptionMenu" into aRedoEvent["exceptionMenuLabel"] put the text of btn "exceptionMenu" into aRedoEvent["exceptionMenuText"] -- put the undo text back put aUndoEvent["eventTitle"] into fld "eventTitle" put aUndoEvent["eventDate"] into fld "eventDate" put aUndoEvent["eventTime"] into fld "eventTime" put aUndoEvent["eventTimeStart"] into fld "eventTimeStart" put aUndoEvent["eventTimeFinish"] into fld "eventTimeFinish" put aUndoEvent["eventDetails"] into fld "eventDetails" put aUndoEvent["startDate"] into fld "startDate" put aUndoEvent["endDate"] into fld "endDate" put aUndoEvent["days"] into fld "days" put aUndoEvent["addException"] into fld "addException" set the label of btn "exceptionMenu" to aUndoEvent["exceptionMenuLabel"] set the text of btn "exceptionMenu" to aUndoEvent["exceptionMenuText"] disable menuItem 1 of btn "Edit" unlock screen end UndoNewEvent --|------------------------------------------------------ --| returns "true" if the aUndoEvent array has content --|------------------------------------------------------ function UndoEventIsNotEmpty put empty into tUndoFlag put the keys of aUndoEvent into tUndoEventKeys repeat for each line tKey in tUndoEventKeys if aUndoEvent[tKey] is not empty then put "true" into tUndoFlag exit repeat end if end repeat if tUndoFlag then -- content was found return "true" else -- no content return "false" end if end UndoEventIsNotEmpty --|------------------------------------------------------ --| change the name of the Undo menu item --| to reflect the exact undo operation that is available --| --| pUndoType describes the new name of the Undo menu item --| aUndoEvent is a local variable as well as an array --|------------------------------------------------------ command UndoMenuNameChange pUndoType put the text of btn "Edit" into tEditMenuItems switch pUndoType case "event" put "&Undo Clear Event/Z" into line 1 of tEditMenuItems break case "Delete Event" put "&Undo Delete Event/Z" into line 1 of tEditMenuItems break case "New File" put "&Undo New File/Z" into line 1 of tEditMenuItems break case "Text Formatting" put "&Undo Text Formatting/Z" into line 1 of tEditMenuItems break default -- engine will undo basic text changes put "&Undo/Z" into line 1 of tEditMenuItems break end switch set the text of of btn "Edit" to tEditMenuItems enable menuItem 1 of btn "Edit" end UndoMenuNameChange on textChanged UndoMenuNameChange end textChanged --|------------------------------------------------------ --| pNewEvent -- means it came from a button click --| and not a mouseUp message from the data grid --| --| aUndoEvent is a local variable as well as an array --|------------------------------------------------------ command ClearItOut pNewEvent lock screen -- remove hilite from days of the week repeat with x = 1 to the number of btns in grp "DayNamesGrp" set the hilite of btn x of grp "DayNamesGrp" to "false" end repeat put empty into aUndoEvent -- record the undo information put fld "eventTitle" into aUndoEvent["eventTitle"] put fld "eventDate" into aUndoEvent["eventDate"] put fld "eventTime" into aUndoEvent["eventTime"] put fld "eventTimeStart" into aUndoEvent["eventTimeStart"] put fld "eventTimeFinish" into aUndoEvent["eventTimeFinish"] put fld "eventDetails" into aUndoEvent["eventDetails"] put fld "startDate" into aUndoEvent["startDate"] put fld "endDate" into aUndoEvent["endDate"] put fld "days" into aUndoEvent["days"] put fld "addException" into aUndoEvent["addException"] put the label of btn "exceptionMenu" into aUndoEvent["exceptionMenuLabel"] put the text of btn "exceptionMenu" into aUndoEvent["exceptionMenuText"] if UndoEventIsNotEmpty() then UndoMenuNameChange "event" else disable menuItem 1 of btn "Edit" end if DeleteEventMenuItemEnableDisable "disable" -- single event put empty into fld "eventTitle" put empty into fld "eventDate" put empty into fld "eventTime" put empty into fld "eventTimeStart" put empty into fld "eventTimeFinish" put empty into fld "eventDetails" -- repeating event put empty into fld "startDate" put empty into fld "endDate" put empty into fld "days" put empty into fld "addException" set the label of btn "exceptionMenu" to empty set the text of btn "exceptionMenu" to empty set the showBorder of btn "startDate" to "false" -- in case a warning border was shown set the showBorder of btn "endDate" to "false" -- in case a warning border was shown -- get rid of a possible warning border set the threeD of fld "days" to "true" set the borderColor of fld "days" to empty set the borderWidth of fld "days" to 2 hide grp "repeatingDayTypeGrp" hide grp "numberPadGrp" show widget "Calendar" SetWidgetCalendarToToday if pNewEvent then -- get rid of a possible warning border set the threeD of fld "eventTitle" to "true" set the borderColor of fld "eventTitle" to empty set the borderWidth of fld "eventTitle" to 2 -- get rid of a possible warning border set the threeD of fld "eventDate" to "true" set the borderColor of fld "eventDate" to empty set the borderWidth of fld "eventDate" to 2 set the dgHilitedLines of group "EventDataGrid" to empty set the uEventIndexNumToEdit of stack kMain to empty -- init end if set the label of btn "submit" to "Submit New Event" --disable btn "delete" --set the blendlevel of btn "delete" to 50 unlock screen focus on fld "eventTitle" end ClearItOut --|------------------------------------------------------ --| put the clicked date of widget calendar --| into the Event Date field --|------------------------------------------------------ on dateChanged pDate -- first assure that the date is in range put the text of fld "eventListYear" into tAppropriateYear put char 1 to 4 of pDate into tNewEventYear if tNewEventYear is not tAppropriateYear then if tAppropriateYear is empty then answer "Please open a file or create a new one." else answer "This date is outside the range of your" && tAppropriateYear && "events file." end if exit dateChanged end if if IsRepeating() then if the enabled of fld "startDate" then put WidgetDateToEnglishDate(pDate) into fld "startDate" set the showBorder of btn "startDate" to "false" -- in case a warning border was shown end if if the enabled of fld "endDate" then put WidgetDateToEnglishDate(pDate) into fld "endDate" set the showBorder of btn "endDate" to "false" -- in case a warning border was shown end if if the vis of fld "addException" then put WidgetDateToEnglishDate(pDate) into tEnglishDate -- eg: Sat, Mar 6, 2021 put tEnglishDate into fld "addException" put the text of btn "exceptionMenu" into tBtnText set the text of btn "exceptionMenu" to trim(tEnglishDate & CR &tBtnText) set the label of btn "exceptionMenu" to tEnglishDate hide fld "addException" focus on nothing -- CalendarTargetFocus "startDate" -- move the focus off of this object end if else put WidgetDateToEnglishDate(pDate) into fld "eventDate" send "textChanged" to fld "eventDate" -- remove a possible warning border end if end dateChanged function EnglishDateToWidgetDate pEnglishDate -- eg: Sat, Mar 6, 2021 set the itemDel to COMMA -- just being careful if (the number of items of pEnglishDate is not 3) OR (item 3 of pEnglishDate is not a number) OR\ (word 2 of item 2 of pEnglishDate is not a number) then answer "EnglishDateToWidgetDate was expecting something like 'Sat, Mar 6, 2021' but encountered:"&&pEnglishDate exit EnglishDateToWidgetDate end if put word 2 of item 2 of pEnglishDate into tDayNum -- 6 delete word 2 of item 2 of pEnglishDate -- Sat, Mar , 2021 replace SPACE with empty in pEnglishDate -- Sat,Mar,2021 put item 3 of pEnglishDate into tYear -- not used any more!!! put item 2 of pEnglishDate into tAbbrMonthName -- Mar put 0+0 into tMonthNum -- init repeat for each line tMonth in the monthNames add 1 to tMonthNum if tAbbrMonthName is in tMonth then exit repeat end if end repeat return (tYear &"-"& tMonthNum &"-"& tDayNum) -- eg: 2021-03-06 end EnglishDateToWidgetDate function WidgetDateToEnglishDate pWidgetDate -- eg: 2021-03-06 set the itemDel to "-" if (item 1 of pWidgetDate is not a number) OR (item 2 of pWidgetDate is not a number) OR\ (item 3 of pWidgetDate is not a number) then answer "WidgetDateToEnglishDate was expecting 3 numbers but found:"&&pWidgetDate exit WidgetDateToEnglishDate end if put item 1 of pWidgetDate into tYear put item 2 of pWidgetDate into tMonth put item 3 of pWidgetDate into tDay put tMonth &SLASH& tDay &SLASH& tYear into tShortDate convert tShortDate to abbreviated english date -- eg: Sat, Mar 6, 2021 set the itemDel to COMMA return tShortDate end WidgetDateToEnglishDate --|------------------------------------------------------ --| --|------------------------------------------------------ command SetWidgetCalendarToToday put the date into tRawDate --> 2/27/21 set the itemDel to SLASH put "20" & item -1 of tRawDate into tYear put item 2 of tRawDate into tDay put item 1 of tRawDate into tMonth put (tYear &"-"& tMonth &"-"& tDay) into tWidgetDate set the selectedDate of widget "Calendar" to tWidgetDate set the itemDel to COMMA -- tidy end SetWidgetCalendarToToday --|------------------------------------------------------ --| There are multiple objects that the calendar can set. --| When clicking a calendar target, object this commmand --| enables all the associated objects and disables the one --| that should no longer receive the calendar's attention --|------------------------------------------------------ command CalendarTargetFocus pFocusTarget hide grp "repeatingDayTypeGrp" show widget "Calendar" switch pFocusTarget case "startDate" enable fld "startDate" set the blendlevel of fld "startDateLabel" to 0 disable fld "endDate" set the blendlevel of fld "endDateLabel" to 50 hide fld "addException" focus on fld "startDate" break case "endDate" disable fld "startDate" set the blendlevel of fld "startDateLabel" to 50 enable fld "endDate" set the blendlevel of fld "endDateLabel" to 0 hide fld "addException" focus on fld "endDate" break case "addException" -- case "deleteException" put empty into fld "addException" show fld "addException" disable fld "startDate" set the blendlevel of fld "startDateLabel" to 50 disable fld "endDate" set the blendlevel of fld "endDateLabel" to 50 focus on fld "addException" break end switch end CalendarTargetFocus --|------------------------------------------------------ --| Insert a new event (or update an existing one) to the dataGrid --| --| Parameters: string string --| ASCII 29 is used to temporarily escape user-entered CRs --| and ONLY while in the list-field of this editing application. --| -- RS = Record Separator = ASCII 29 --| -- US = Unit Separator = ASCII 30 --| -- LS = Line Separator = ASCII 31 --| Data is stored in a Line Seperated list (LS) --| Each line contains data about a single event --| --| US US US US US --| --| can contain a number from 1-12 or the letter W or M which denotes a repeating event --| a repeating event string is formatted: --| --| W = week event repeats each week --| | | | | | --| DayNums is a PERIOD delimited list where 1=Sunday 2=Monday 3=Tuesday etc... ex: 2.4.6 (Mon,Wed,Fri) --| --| M = month event repeats one day per month --| WeekNum: 1 = first week, 2 = second week ... 5 = fifth week, 6 = last week --| DayNum: (see DayNums above) --| | | | | | --|------------------------------------------------------ command SubmitEvent if the label of button "submit" contains "update" then put the uEventIndexNumToEdit of stack kMain into tIndexNum -- double check that the selected index is the one we are actually editing if (the dgHilitedIndexes of group "EventDataGrid") is not tIndexNum then answer Warning "No update occurred." ResetForNewEvent exit SubmitEvent end if end if put empty into tRepeatingEvent -- init put fld "eventTitle" into tEventTitle if tEventTitle is empty then set the threeD of fld "eventTitle" to "false" set the borderWidth of fld "eventTitle" to 3 set the borderColor of fld "eventTitle" to 255,38,0 exit SubmitEvent end if if IsRepeating() then --START DATE put fld "startDate" into tStartDate -- 2021-02-27 if tStartDate is empty then set the showBorder of btn "startDate" to "true" exit SubmitEvent end if put EnglishDateToWidgetDate(tStartDate) into tStartDate --END DATE put fld "endDate" into tEndDate if tEndDate is empty then -- assume we want the event to run forever (end of year) set the itemDel to "-" put item 1 of tStartDate into tYear put tYear &"-12-31" into tEndDate -- December 31 the target year -- set the showBorder of btn "endDate" to "true" -- exit SubmitEvent else put EnglishDateToWidgetDate(tEndDate) into tEndDate end if -- DAY NAMES put fld "days" into tDayNames -- Mon, Wed, Fri -or- fourth Wed if tDayNames is empty then set the threeD of fld "days" to "false" set the borderWidth of fld "days" to 3 set the borderColor of fld "days" to 255,38,0 exit SubmitEvent end if --EXCEPTIONS put the text of btn "exceptionMenu" into tRawExceptionList -- Sat, Mar 6, 2021 repeat for each line tExceptionDate in tRawExceptionList put EnglishDateToWidgetDate(tExceptionDate) into tWidgetDate -- 2021-03-06 set the itemDel to "-" put item 2 of tWidgetDate into tExceptionMonthNum put item 3 of tWidgetDate into tExceptionDayNum -- repeating dates don't contain a year -- exceptions shouldn't contain a year, either put tExceptionMonthNum &"."& tExceptionDayNum &"^" after tExceptionList end repeat -- tExceptionList delete char -1 of tExceptionList -- trailing itemDel "^" set the itemDel to "-" put item 1 of tStartDate into tYear put item 2 of tStartDate into tStartMonth put item 3 of tStartDate into tStartDay put item 2 of tEndDate into tEndMonth put item 3 of tEndDate into tEndDay set the itemDel to COMMA if word 1 of tDayNames is among the items of "first,second,third,fourth,fifth,last" then put WeekOrdinalToNum(word 1 of tDayNames) &"."& DayNamesToNums(word 2 of tDayNames) into tDays put "M" into tRepeatType else put DayNamesToNums(tDayNames) into tDays -- we can't keeep the COMMAs since they will bork the data grid replace COMMA with "." in tDays put "W" into tRepeatType end if -- build the repeating event string put "|" into tPipe -- this entire string will go where tMonthNum goes in a normal event put tRepeatType &tPipe& tStartMonth &tPipe& tStartDay &tPipe& tEndMonth &tPipe& tEndDay &tPipe& tDays &tPipe& tExceptionList into tRepeatingEvent else put fld "eventDate" into tEventDate -- Sat, Feb 27, 2021 if tEventDate is empty then set the threeD of fld "eventDate" to "false" set the borderWidth of fld "eventDate" to 3 set the borderColor of fld "eventDate" to 255,38,0 exit SubmitEvent end if put EnglishDateToWidgetDate(tEventDate) into tEventDate -- 2021-02-27 end if put fld "eventTimeStart" into tStartTime put fld "eventTimeFinish" into tFinishTime if (tStartTime is not empty) AND (tFinishTime is not empty) then put tStartTime &&"-"&& tFinishTime into tEventTime else put tStartTime & tFinishTime into tEventTime end if -- put fld "eventTime" into tEventTime put fld "eventDetails" into tEventDetails -- put numToNativeChar(29) into tGS -- Group Separator put numToNativeChar(30) into tUS -- Unit Separator put numToNativeChar(31) into tLS -- Line Separator -- sanitize a little -- TAB is not allowed -- CR is allowed ONLY in tEventDetails replace TAB with empty in tEventTitle replace CR with SPACE in tEventTitle replace TAB with empty in tEventDate replace CR with SPACE in tEventDate replace TAB with empty in tEventTime replace CR with SPACE in tEventTime replace TAB with empty in tEventDetails -- add CRs back at the single event level -- when editing or right when inserting into the calendar -- we will actually still need to re-insert the tLS -- after converting to HTMLText... but it is easier not to use CR replace CR with tLS in tEventDetails -- tRawEvents* wont be run through HTMLText... so, no entities put tEventTitle into tRawEventTitle put tEventDetails into tRawEventDetails -- change Unicode glyphs to htmlEntities per user preferences if the hilite of btn "UseHTMLEntities" then -- use the templateField to create HTMLEntities out of Unicode characters -- (we assume that there will be no "special characters" to process in the date) put tEventTitle into the templateField -- remove the

tags put char 4 to -5 of (the HTMLText of the templateField) into tEventTitle put tEventTime into the templateField put char 4 to -5 of (the HTMLText of the templateField) into tEventTime put tEventDetails into the templateField put char 4 to -5 of (the HTMLText of the templateField) into tEventDetails -- -- we DON'T want the angle brackets to be HTMLEntities -- -- because then we can't add HTML tags like to get additional formatting -- --> actually, this turns out to be easier to solve on the server side -- --> because the data grid editing will also need to be changed... less easy -- replace "<" with "<" in tEventTitle -- replace ">" with ">" in tEventTitle -- replace "<" with "<" in tEventDetails -- replace ">" with ">" in tEventDetails -- our Line Separators were coverted to HTML entities -- so put back the Line Separators replace "" with tLS in tEventDetails end if set the itemDel to "-" if not IsRepeating() then put item 1 of tEventDate into tYear put item 2 of tEventDate into tMonthNum put item -1 of tEventDate into tDayNum end if -- if creating a NEW FILE for a NEW YEAR if fld "eventListYear" is empty then put tYear into fld "eventListYear" end if if tYear is not (the text of fld "eventListYear") then answer Warning "The year doesn't match the rest of the document." exit SubmitEvent end if if tRepeatingEvent is not empty then put tRepeatingEvent into tMonthNum end if -- put ALL of the processed data into our custom format --> tRawEvent -- Everything about the event can be recreated from this string -- The other event entries are created just for nice UI display in the datagrid (Month names, no HTML entities...) put tYear & tUS & tMonthNum & tUS & tDayNum & tUS & tEventTitle & tUS & tEventTime & tUS & tEventDetails into tRawEvent set the itemDel to COMMA -- tidy -- build something for the "Month" column of the data grid if IsRepeating() then -- repeating event if tRepeatType is "W" then replace SPACE with empty in tDayNames repeat for each item tDayName in tDayNames if tDayName is "Thu" then put char 1 to 2 of tDayName&COMMA after tEventDate else put char 1 of tDayName&COMMA after tEventDate end if end repeat delete char -1 of tEventDate else -- tRepeatType is "M" put tDayNames into tEventDate end if else -- single event put line tMonthNum of the abbreviated english monthNames into tMonthName put (tMonthName&"."&&tDayNum) into tEventDate end if if the label of button "submit" contains "update" then -- update an existing event -- fill in the dataGrid columns for this index (row) dispatch "SetDataOfIndex" to group "EventDataGrid" with tIndexNum, "title", tRawEventTitle dispatch "SetDataOfIndex" to group "EventDataGrid" with tIndexNum, "month", tEventDate dispatch "SetDataOfIndex" to group "EventDataGrid" with tIndexNum, "description", tRawEventDetails dispatch "SetDataOfIndex" to group "EventDataGrid" with tIndexNum, "rawEvent", tRawEvent dispatch "RefreshIndex" to group "EventDataGrid" with tIndexNum else -- create a new event put 1 into tLine dispatch "AddLine" to group "EventDataGrid" with tRawEventTitle, "title", tLine put the dgIndexOfLine[tLine] of group "EventDataGrid" into tNewIndex dispatch "SetDataOfIndex" to group "EventDataGrid" with tNewIndex, "month", tEventDate dispatch "SetDataOfIndex" to group "EventDataGrid" with tNewIndex, "description", tRawEventDetails dispatch "SetDataOfIndex" to group "EventDataGrid" with tNewIndex, "rawEvent", tRawEvent dispatch "RefreshLine" to group "EventDataGrid" with tLine end if if IsRepeating() then -- don't keep the event data ClearItOut end if put "true" into tDontResetCalendar ResetForNewEvent tDontResetCalendar FileState "dirty" enable group "EventDataGrid" -- just in case DeleteEventMenuItemEnableDisable "disable" end SubmitEvent --|--------------------------------------------- --| used to enable or disable the Delete Event menu item --| pState is either "disable" or "enable" --|--------------------------------------------- command DeleteEventMenuItemEnableDisable pState put the text of btn "Edit" into tEditMenuText put 0 into tLineCounter -- init if pState is "disable" then repeat for each line tMenuItem in tEditMenuText add 1 to tLineCounter if "Delete Event" is in tMenuItem then disable menuItem tLineCounter of btn "Edit" end if end repeat else -- assume "enable" repeat for each line tMenuItem in tEditMenuText add 1 to tLineCounter if "Delete Event" is in tMenuItem then enable menuItem tLineCounter of btn "Edit" end if end repeat end if end DeleteEventMenuItemEnableDisable function IsRepeating return (the hilite of btn "repeating") end IsRepeating --|--------------------------------------------- --| adds (or removes) a repeating day --| to the list of repeating days --| --| called from transparent buttons covering the --| calendar widget's days-of-the-week labels --|--------------------------------------------- command UseThisRepeatingDay -- use only if editing a Repeating Event if not the hilite of btn "repeating" then set the hilite of the target to "false" exit UseThisRepeatingDay end if -- get rid of a possible warning border set the threeD of fld "days" to "true" set the borderColor of fld "days" to empty set the borderWidth of fld "days" to 2 --put the label of the target into tDay put the short name of the target into tDay -- ex: "repeatingThursday" replace "repeating" with empty in tDay -- ex: "Thursday" delete char 4 to -1 of tDay -- ex: "Thu" if the label of btn "repeatingWeekOrMonth" is "Monthly" then -- allow only one day to be selected -- remove hilite from days of the week repeat with x = 1 to the number of btns in grp "DayNamesGrp" set the hilite of btn x of grp "DayNamesGrp" to "false" end repeat put (the label of btn "repeatingDayType") && tDay into fld "days" set the hilite of the target to "true" exit UseThisRepeatingDay end if put fld "days" into tListOfDays -- remove cosmetic SPACEs after COMMAs replace SPACE with empty in tListOfDays if (tDay is among the items of tListOfDays) then -- if this day is already in the list -- remove it from the list replace tDay with empty in tListOfDays replace ",," with COMMA in tListOfDays if char 1 of tListOfDays is COMMA then delete char 1 of tListOfDays end if if char -1 of tListOfDays is COMMA then delete char -1 of tListOfDays end if put trim(tListOfDays) into tListOfDays -- remove possible leading/trailing space set the hilite of the target to "false" else -- add it to the list and order the list put the num of items of tListOfDays into tNumOfDay put tDay into item (tNumOfDay +1) of tListOfDays put DayNamesToNums(tListOfDays) into tListOfDayNums sort items of tListOfDayNums ascending numeric put DayNumsToNames(tListOfDayNums) into tListOfDays set the hilite of the target to "true" end if replace COMMA with (COMMA & SPACE) in tListOfDays put tListOfDays into fld "days" end UseThisRepeatingDay --|--------------------------------------------- --| Set the Frequency Option Menu to reflect --| the selected (to-be-edited) repeating event --|--------------------------------------------- command SetFrequencyOptionMenu pMenuItem put pMenuItem into tMenuItem if pMenuItem is "M" then put "Monthly" into tMenuItem end if if pMenuItem is "W" then put "Weekly" into tMenuItem end if put the text of btn "repeatingWeekOrMonth" into tMenuItems put 1 into tLineCounter repeat for each line tLine in tMenuItems if tMenuItem is tLine then set the menuHistory of btn "repeatingWeekOrMonth" to tLineCounter exit SetFrequencyOptionMenu end if add 1 to tLineCounter end repeat answer "SetFrequencyOptionMenu encountered and error:" &&pMenuItem&&"was not an expected menu item." end SetFrequencyOptionMenu --|--------------------------------------------- --| Set the Frequency Option Menu to reflect --| the selected (to-be-edited) repeating event --|--------------------------------------------- command SetOrdinalOptionMenu pMenuItem put the text of btn "repeatingDayType" into tMenuItems put 1 into tLineCounter repeat for each line tLine in tMenuItems if pMenuItem is tLine then set the menuHistory of btn "repeatingDayType" to tLineCounter exit SetOrdinalOptionMenu end if add 1 to tLineCounter end repeat answer "SetOrdinalOptionMenu encountered and error:" &&pMenuItem&&"was not an expected menu item." end SetOrdinalOptionMenu --|--------------------------------------------- --| Hilite the Weekday buttons to reflect --| the selected (to-be-edited) repeating event --| pDayNums is a comma delimited list of week numbers --|--------------------------------------------- command SetDayHilites pDayNums -- turn them all off repeat with x = 1 to 7 set the hilite of btn x of grp "DayNamesGrp" to "false" end repeat -- hilite just the ones we want repeat for each item tDayNum in pDayNums hilite btn tDayNum of grp "DayNamesGrp" end repeat end SetDayHilites --|--------------------------------------------- --| convert ordinals to week numbers --|--------------------------------------------- function WeekOrdinalToNum pOrdinal put "first,second,third,fourth,fifth,last" into tOrdinals put 0 into tWeekNum repeat for each item tOrdinal in tOrdinals add 1 to tWeekNum if tOrdinal is pOrdinal then return tWeekNum end if end repeat answer "WeekOrdinalToNum() reported an error:"&& pOrdinal && "is not an expected ordinal" end WeekOrdinalToNum --|--------------------------------------------- --| convert week numbers to ordinals --|--------------------------------------------- function WeekNumsToOrdinals pWeekNum -- harder to read version put "first,second,third,fourth,fifth,last" into tOrdinals if (pWeekNum < 1) OR (pWeekNum > the num of items of tOrdinals) then answer "WeekNumsToOrdinals() encountered an error:"&& pWeekNum &&"is not among the expected week numbers." exit WeekNumsToOrdinals end if return item pWeekNum of tOrdinals answer "WeekNumsToOrdinals() encountered an error:"&& pWeekNum &&"is not among the expected week numbers." end WeekNumsToOrdinals --|--------------------------------------------- --| convert the days of the week to numbers 1-7 --|--------------------------------------------- function DayNamesToNums pDays -- harder to read version put "Sun,Mon,Tue,Wed,Thu,Fri,Sat" into tDaysOfTheWeek repeat for each item tDay in pDays put 1 into tItemCounter repeat for each item tAbbreviatedDayName in tDaysOfTheWeek if tDay contains tAbbreviatedDayName then put tItemCounter & COMMA after tNums end if add 1 to tItemCounter end repeat end repeat ----------------------------------------------------------- -- -- easier to read version -- repeat for each item tDay in pDays -- switch -- case tDay contains "Sun" -- put 1 & COMMA after tNums -- break -- case tDay contains "Mon" -- put 2 & COMMA after tNums -- break -- case tDay contains "Tue" -- put 3 & COMMA after tNums -- break -- case tDay contains "Wed" -- put 4 & COMMA after tNums -- break -- case tDay contains "Thu" -- put 5 & COMMA after tNums -- break -- case tDay contains "Fri" -- put 6 & COMMA after tNums -- break -- case tDay contains "Sat" -- put 7 & COMMA after tNums -- break -- end switch -- end repeat delete char -1 of tNums -- remove trailing comma return tNums end DayNamesToNums --|--------------------------------------------- --| convert the numbers 1-7 to short names for days of the week --| "short" meaning 1 character with the exception of Thursday = Th --| we use this for displaying in the Date column of the data grid --| There is a similar function below that returns slightly longer day names --|--------------------------------------------- function DayNumsToShortNames pNums --breakpoint put "Sun,Mon,Tue,Wed,Thu,Fri,Sat" into tDaysOfTheWeek repeat for each item tNum in pNums if tNum is 5 then put "Th," after tDays else put (char 1 of (item tNum of tDaysOfTheWeek)) & COMMA after tDays end if end repeat delete char -1 of tDays -- remove trailing comma return tDays end DayNumsToShortNames --|--------------------------------------------- --| convert the numbers 1-7 to abbreviated days of the week --| --| there is a similar function above that returns --| even shorter day-name abbreviations --|--------------------------------------------- function DayNumsToNames pNums --breakpoint put "Sun,Mon,Tue,Wed,Thu,Fri,Sat" into tDaysOfTheWeek repeat for each item tNum in pNums put (item tNum of tDaysOfTheWeek) & COMMA after tDays end repeat delete char -1 of tDays -- remove trailing comma return tDays -- repeat for each item tNum in pNums -- switch tNum -- case 1 -- put "Sun" & COMMA after tDays -- break -- case 2 -- put "Sun" & COMMA after tDays -- break -- case 3 -- put "Sun" & COMMA after tDays -- break -- case 4 -- put "Sun" & COMMA after tDays -- break -- case 5 -- put "Sun" & COMMA after tDays -- break -- case 6 -- put "Sun" & COMMA after tDays -- break -- case 7 -- put "Sun" & COMMA after tDays -- break -- end switch -- end repeat -- delete item -1 of tDays -- remove trailing comma -- return tDays end DayNumsToNames --|-------------------------------------------------- --| sets the custom prop uFileState as [clean | dirty] --| and sets a corresponding "light" to green, yellow --| green = 3394 yellow = 3393 gray = 4117 --| does NOT set the EMPTY state --> gray "light" --|-------------------------------------------------- command FileState pState if pState is "clean" then set the icon of btn "fileStateIndicator" to 3394 set the uFileState of stack kMain to "clean" else -- dirty / yellow set the icon of btn "fileStateIndicator" to 3393 set the icon of btn "writeFile" to WriteIcon() set the uFileState of stack kMain to "dirty" end if end FileState --|-------------------------------------------------- --| returns the ID of an image to be used --| as the icon for the btn "writeFile" --|-------------------------------------------------- function WriteIcon if (the hilite of btn "publishLocal") AND (the hilite of btn "publishRemote") then return 24854 -- cloud&floppy-upload end if if (the hilite of btn "publishLocal") AND (not the hilite of btn "publishRemote") then return 24855 --floppy-upload end if if (the hilite of btn "publishRemote") AND (not the hilite of btn "publishLocal") then return 1025 --cloud-upload end if end WriteIcon --|-------------------------------------------------- --| --|-------------------------------------------------- command DeleteEvent if the label of btn "submit" contains "update" then put the uEventIndexNumToEdit of stack kMain into tIndexNum -- double check that the selected index is the one we are actually editing if (the dgHilitedIndexes of group "EventDataGrid") is not tIndexNum then exit DeleteEvent end if end if put the dgText of group "EventDataGrid" into tUndoText -- just in case -- delete dispatch "DeleteIndex" to group "EventDataGrid" with tIndexNum -- delete the dataGrid entry dispatch "RefreshLine" to group "EventDataGrid" with tIndexNum -- update the dataGrid visual set the dgHilitedLines of group "EventDataGrid" to empty -- init set the uEventIndexNumToEdit of stack kMain to empty -- init set the label of btn "submit" to "Submit New Event" set the uUndoEvent of stack kMain to tUndoText --set the label of btn "delete" to "Undo" UndoMenuNameChange "Delete Event" --set the textAlign of btn "delete" to "center" --set the icon of btn "delete" to 1032 if (the dgdata of grp "EventDataGrid" is not empty) then enable group "EventDataGrid" else disable group "EventDataGrid" -- just in case end if -- disable the "Delete Event" menu item since there will no longer be a selected event put the text of btn "Edit" into tEditMenuText put 0 into tLineCounter -- init repeat for each line tMenuItem in tEditMenuText add 1 to tLineCounter if tMenuItem is "Delete Event" then disable menuItem tLineCounter of btn "Edit" end if end repeat end DeleteEvent command UndoDeletedEvent set the dgText of group "EventDataGrid" to (the uUndoEvent of stack kMain) -- we might be undoing a "New Document" event put numToNativeChar(30) into tUS -- Unit Separator put the uUndoEvent of stack kMain into tRawEventData set the itemDel to TAB put item -1 of tRawEventData into tRawEventData set the itemDel to tUS put item 1 of tRawEventData into tEventYear if (fld "eventListYear" is not tEventYear) then put tEventYear into fld "eventListYear" set the itemDel to COMMA -- tidy end if --disable btn "delete" --set the blendlevel of btn "delete" to 50 set the uUndoEvent of stack kMain to empty disable menuItem 1 of btn "Edit" enable group "EventDataGrid" -- in case the dg had been completely emptied FileState "dirty" -- probably... too lazy to actually track it :-) end UndoDeletedEvent --|-------------------------------------------------- --| pNotCalendar is optional --|-------------------------------------------------- command ResetForNewEvent pNotCalendar -- if a line was selected it isn't anymore -- so remove the "Update" features if pNotCalendar then -- don't clear the calendar else SetWidgetCalendarToToday end if set the uEventIndexNumToEdit of stack kMain to empty -- init set the label of btn "submit" to "Submit New Event" --disable btn "delete" --set the blendlevel of btn "delete" to 50 end ResetForNewEvent --|-------------------------------------------------- --| saves the file to... --| pSaveAsDuplicate is boolean (true to "Save As...") --|-------------------------------------------------- command PublishFile pSaveAsDuplicate if (not the hilite of btn "publishLocal") AND (not the hilite of btn "publishRemote") then answer "No destination selected." end if -- get the data put the dgText of group "EventDataGrid" into tDataGridText set the itemDel to TAB put empty into tRawEventList repeat for each line tEvent in tDataGridText replace CR with empty in tEvent -- sanitize put item -1 of tEvent & CR after tRawEventList end repeat delete char -1 of tRawEventList -- remove trailing CR put empty into tLocalFolderPath -- init put fld "localPathPref" into tLocalFolderPath if tLocalFolderPath is empty then if the hilite of btn "publishLocal" then answer folder "Save event data locally..." as sheet put the result into tResult put it into tLocalFolderPath if (tResult is "cancel") OR (it is empty) then exit PublishFile end if end if end if -- build the fileName if pSaveAsDuplicate then -- save to a new file ask "What year?" if (the result is "cancel") OR (it is empty) then exit PublishFile end if put it into tYear if (tYear is not a number) OR\ ((the number of chars of tYear)<>2 AND (the number of chars of tYear)<>4) then answer "Error: Please enter a 4 digit year" exit PublishFile end if if (the number of chars of tYear)=2 then -- turn 2 digit year into 4 digit put 2000 + tYear into tYear end if -- update references to the previously recorded year (in the file data) to the "new" year put numToNativeChar(30) into tUS -- Unit Separator set the itemDel to tUS put item 1 of tRawEventList into tOldYear set the itemDel to COMMA -- tidy replace tOldYear with tYear in tDataGridText NewDocument tYear -- tRawEventList is sanitized but no longer prepped to be put into a DG -- tDataGridText has had the year changed but is still formatted to go into the DG if tRawEventList is not empty then set the dgText of group "EventDataGrid" to tDataGridText enable group "EventDataGrid" end if exit PublishFile else -- regular save put fld "eventListYear" into tYear if (tYear is not a number) then -- try to extract it from the data put numToNativeChar(30) into tUS -- Unit Separator set the itemDel to tUS put item 1 of tRawEventList into tYear set the itemDel to COMMA -- tidy if (tYear is not a number) then answer Warning "No year. Would you like to enter a year?" with "Yes" and "Cancel" if it is "Cancel" then exit PublishFile end if ask "Enter a 4 digit year number" if (it is not a number) then exit PublishFile end if put it into tYear end if end if end if -- pSaveAsDuplicate or regular save put (tYear &"_calendar_events.txt") into tFileName -- include tFileName to construct a full tFilePath put tLocalFolderPath & SLASH & tFileName into tFilePath -- in case the user changes the name of the local file put empty into tOldFilePath -- init if the hilite of btn "publishLocal" then if the uRemoteSourceFile of stack kMain then if there is a file tFilePath then answer "Do you want to overwrite the local file with this remote source-data?" with "Overwrite" and "Rename" and "Cancel" put it into tUserChoice if tUserChoice is "Cancel" then exit PublishFile end if if tUserChoice is "Rename" then -- NOTE / TODO -- This should address the year contained inside the data -- in addition to the name of the file ask "New filename:" with "[New Name]_calendar_events.txt" if (the result is "Cancel") OR (it is empty) then exit PublishFile end if put it into tNewFileName if (char -4 to -1 of tNewFileName) is not ".txt" then put ".txt" after tNewFileName end if put tFilePath into tOldFilePath put the itemDel into tOldItemDel set the itemDel to SLASH put tNewFileName into last item of tFilePath set the itemDel to tOldItemDel -- put things back where they were end if end if end if put textEncode(tRawEventList,"UTF-8") into tUTF8EventList -- save locally -- make sure that the directory path actually exists put empty into tLocalErrorReport if there is not a folder tLocalFolderPath then put ("There is no local folder:"&&tLocalFolderPath) into tLocalErrorReport end if put tUTF8EventList into URL ("file:"&tFilePath) put the result into tResult if tResult is not empty then answer "There was a problem saving the file locally:" &CR& tResult end if FileState "clean" -- NOTE: this may be confusing if there is a later error during FTP set the icon of btn "writeFile" to 24891 --floppy-check -- cloud&floppy-check -- 5257 --cloud-check end if --|-------------------------------------------------- --| ftp to server with tsNet --|-------------------------------------------------- put empty into field "Server Response" -- init if the hilite of btn "publishRemote" then if tOldFilePath is not empty then put tOldFilePath into tFilePath end if -- Store the FTP server details in some variables put fld "ftpServerPref" into tFtpServer put fld "ftpDirectoryPathPref" into tFtpDirectory if char -1 of tFtpDirectory is SLASH then delete char -1 of tFtpDirectory end if if (tFtpServer is empty) OR (tFtpDirectory is empty) then answer CR&"Enter ftp details in Preferences" exit PublishFile end if ProgressBar "start" -- decide if we should use tsNet or LibURL -- NOTE: the LibURL section is NOT working put empty into tHeaders -- init -- put "tFtpServer:"&&tFtpServer&CR& "tFtpDirectory:"&&tFtpDirectory&CR&"tHeaders:"&&tHeaders&CR&"tRecvHeaders:"&&tRecvHeaders&CR&"tResult:"&&tResult&CR&"tBytes:"&&tBytes&CR&"tSettings:"&&tSettings --TEST!!! if the editionType is "professional" then tsNetInit -- required before using any tsNet features... especially since we explicityly close tsNet at the end -- changing the last two params to zeros will remove the low speed time and transfer limits tsNetSetTimeouts 60, 0, 300000, 60000, 30, 1000 -- The tSettings array can be used to provide additional configuration details, -- in this case the username and password to use when transferring the files via FTP put fld "ftpUserNamePref" into tSettings["username"] put fld "ftpPasswordPref" into tSettings["password"] -- To force FTP SSL mode, we use the following additional setting put "true" into tSettings["use_ssl"] -- Get the file name of the file they selected (everything past the last slash) set the itemDel to SLASH put item -1 of tFilePath into tFileName -- -- Since we are using tsNetUploadSync, -- -- we would normally retrieve the contents of a file and -- -- store it in a variable for uploading -- put URL("file:" & tFilePath) into tFileContents -- but in this case we aren't uploading a "file", we are directly publishing a variable put textEncode(tRawEventList,"UTF-8") into tFileContents -- Let the user know that the transfer is starting put "Uploading file" && tFileName && "to" && tFtpServer & tFtpDirectory & cr after field "Server Response" -- Perform the upload put tsNetUploadSync(tFtpServer & tFtpDirectory & slash & tFileName, tHeaders, tFileContents, tRecvHeaders, tResult, tBytes, tSettings) into tOutput ---------------------------------------------------------- -- Output the response to the user put "Transfer complete with server response code" && tResult &CR after field "Server Response" put the result into tResult put CR&"tRecvHeaders"&CR& tRecvHeaders after field "Server Response" put ("tsNetGetTimeouts:"&& tsNetGetTimeouts()) after field "Server Response" if "File successfully transferred" is not in tRecvHeaders then -- FAIL put "--------- Server Responses ---------"&CR before field "Server Response" show field "Server Response" -- so we can see what the error might be else -- SUCCESS if the hilite of btn "publishLocal" then set the icon of btn "writeFile" to 24890 -- cloud&floppy-check -- 5257 --cloud-check 24891 --floppy-check else set the icon of btn "writeFile" to 5257 --cloud-check 24891 --floppy-check 24890 -- cloud&floppy-check end if end if ProgressBar "stop" FileState "clean" tsNetClose else -- editionType is Commercial, Community or Indy version --------------------------------------------------------------------------- -- libURLftpUploadFile -- THIS SECTION DOES NOT WORK -- haven't figured out what I've munged up answer "This section of the PublishEvent handler attempts to use 'libURL' (rather than tsNet) with versions of LiveCode where getting the editionType does not return 'professional'... "&&\ "unfortuantely, my scripting for LibURL appears to be incorrect. Perhaps you can find my mistake." with "edit script" AND "Ok" if it is "edit script" then edit the script of this stack at 1320,1 -- at [line #] COMMA [column #] exit to top end if --------------------------------------------------------------------------- tsNetClose -- make sure that tsNet is unloaded get libURLVersion() -- tsNetInit -- libURLSetFTPMode "active" -- this could be made into a preference setting -- create a temporary local file that we can FTP -- though this isn't a neccessary method in this case -- as we can proabably just send it from a variable put specialFolderPath("temporary")&SLASH&"elsoCalendarEvents" into tTempPath -- make sure that the temp folder we will use exists if there is not a folder tTempPath then create folder tTempPath put the result into tResult if tResult is not empty then answer warning "PublishFile FTP failed. The temporary 'elsoCalendarEvents' directory could not be created." with "Understood" exit PublishFile end if end if put (tTempPath & SLASH & tFileName) into tTempLocalFilePathForUpload put textEncode(tRawEventList,"UTF-8") into tFileContents -- save to (local) temp file put tFileContents into URL ("file:"&tTempLocalFilePathForUpload) put the result into tResult if tResult is not empty then answer "There was a problem saving the temp file locally:" &CR& tResult exit PublishFile end if if there is not a file tTempLocalFilePathForUpload then answer "There was a problem creating the local temp file." exit PublishFile end if put (tFtpDirectory & SLASH & tFileName) into tFtpFilePath put urlEncode(fld "ftpUserNamePref") into tUserName put urlEncode(fld "ftpPasswordPref") into tPassword replace "ftp://" with empty in tFtpServer -- TSNet and LibURL have different construction methods -- replace tUserName with empty in tFtpServer put ("ftp://" & tUserName & ":" & tPassword & "@" & tFtpServer & tFtpFilePath) into tFTPDestination -- answer tTempLocalFilePathForUpload &CR& tFTPDestination --TEST!!! put "libURLftpUploadFile - server response:"&CR&CR into fld "Server Response" -- init libURLSetStatusCallback "uploadProgress", the long ID of me --> NOT WORKING <-- --> put tFileContents into url (tFTPDestination) libURLftpUpload tFileContents,tFTPDestination,"uploadComplete" --> libURLftpUploadFile tTempLocalFilePathForUpload, tFTPDestination, "uploadComplete" -- close the "spinner" in case we error in a way -- that doesn't trigger the progress indicator to stop send ProgressBar to me in 8 seconds end if -- version is Business edition / Community or Indy end if end PublishFile --|-------------------------------------------------- --| Stop the progressbar if the upload completes --| sent from PublishFile as LibURL callback --|-------------------------------------------------- on uploadComplete pURL, pStatus ProgressBar "stop" put "Status Update:" && pStatus && pURL &CR after fld "Server Response" if "error" is in pStatus then show fld "Server Response" unload URL pURL exit uploadComplete end if if "timeout" is in pStatus then show fld "Server Response" unload URL pURL exit uploadComplete end if FileState "clean" end uploadComplete --|-------------------------------------------------- --| could be used to construct a progress bar --| sent from PublishFile as LibURL callback --|-------------------------------------------------- on uploadProgress pURL, pStatus put "Status Update:" && pStatus && pURL &CR after fld "Server Response" if "error" is in pStatus then show fld "Server Response" unload URL pURL ProgressBar "stop" end if if "timeout" is in pStatus then show fld "Server Response" unload URL pURL ProgressBar "stop" end if end uploadProgress --|-------------------------------------------------- --| For PUBLISHING TO web --| show and hide an idetermenent progress indicator --| param pAction can be "start" ["stop" or empty... anything else] --|-------------------------------------------------- command ProgressBar pAction if pAction is "start" then -- show widget "Spinner" set the currentFrame of img "progress" to 0 show img "progress" -- animated GIF set the repeatCount of img "progress" to 500 else -- hide widget "Spinner" set the repeatCount of img "progress" to 0 set the currentFrame of img "progress" to 0 hide img "progress" -- animated GIF end if end ProgressBar --|-------------------------------------------------- --| For READING FROM web --| show and hide an idetermenent progress indicator --| param pAction can be "start" ["stop" or empty... anything else] --|-------------------------------------------------- command RetryProgress pAction if pAction is "start" then -- show widget "Spinner" set the currentFrame of img "retryProgress" to 0 show img "retryProgress" -- animated GIF set the repeatCount of img "retryProgress" to 500 else -- hide widget "Spinner" set the repeatCount of img "retryProgress" to 0 set the currentFrame of img "retryProgress" to 0 hide img "retryProgress" -- animated GIF end if end RetryProgress --|----------------------------------------------------------------- --| Displays or hides the preferences group --|----------------------------------------------------------------- command InformationSheet if the vis of grp "informationGrp" then put the loc of grp "informationGrp" into tStart put (item 1 of tStart &COMMA& 0-(the height of grp "informationGrp" +20)) into tFinish move grp "informationGrp" from tStart to tFinish in 500 millisec hide grp "informationGrp" else if the vis of grp "preferencesGrp" then PreferencesSheet end if put the width of stack kMain div 2 into tCenter put the loc of grp "informationGrp" into tStart put tCenter into item 1 of tStart set the loc of grp "informationGrp" to tStart show grp "informationGrp" set the menuHistory of btn "InformationTab" to 1 -- show "General" tab set the visited of char 1 to -1 of fld "Elementary91ֱLink" to "false" put (item 1 of tStart &COMMA& ((the height of grp "informationGrp") div 2 -10)) into tFinish move grp "informationGrp" from tStart to tFinish in 500 millisec end if end InformationSheet --|----------------------------------------------------------------- --| Displays or hides the preferences group --|----------------------------------------------------------------- command PreferencesSheet pSave -- hide Preferences if the vis of grp "preferencesGrp" then put the loc of grp "preferencesGrp" into tStart put (item 1 of tStart &COMMA& 0-(the height of grp "preferencesGrp" +20)) into tFinish move grp "preferencesGrp" from tStart to tFinish in 500 millisec hide grp "preferencesGrp" if not pSave then ResetPreferences end if PreferenceWrite -- even if not pSave the "Current Site" could have changed put the text of fld "siteName" into tPreviousSiteName set the text of fld "siteName" to the uCurrentSiteName of stack kMain --or we could use the label of btn "siteNames" if the text of fld "siteName" is not tPreviousSiteName then -- the site has changed... clear any previous site data from the dataGrid set the dgText of group "EventDataGrid" to empty -- init set the dgHilitedLines of group "EventDataGrid" to empty -- init (needed?) end if set the uNewSite of btn "SiteNames" to "false" enable btn "openPreferences" set the blendLevel of btn "openPreferences" to 0 else -- show Preferences if the vis of grp "informationGrp" then InformationSheet end if -- disable the "Gear" button -- we only want to close the sheet -- by clicking the "Save" or "Cancel" button disable btn "openPreferences" -- the Save btn will be disabled until something gets changed -- FYI: uCurrentSiteName gets set anytime -- the OptionMenu gets changed -- with no dependence on the Save button disable btn "setPref" set the label of btn "cancelPref" to "Close" -- will switch to "Cancel" if "Save" is enabled set the customPropertySet of stack kMain to empty -- default set put the uCurrentSiteName of stack kMain into tCurrentSiteName -- build the list of site names put the customPropertySets of stack "Calendar Events" into tCustomPropertySets repeat for each line tLine in tCustomPropertySets if (tLine begins with "cRev") then next repeat end if put tLine &CR after tListOfSiteNames end repeat put RemoveIncorrectSiteNames(tListOfSiteNames) into tListOfSiteNames put RemoveIncorrectSiteNames(tCurrentSiteName) into tCurrentSiteName if (tCurrentSiteName is not empty) AND (tCurrentSiteName is not among the lines of tListOfSiteNames) then -- error answer "The current site name,"&&tCurrentSiteName&&"is not among the list of site names." end if put "false" into tAreNoSites -- init if (the num of lines of trim(tListOfSiteNames)) < 1 then put "true" into tAreNoSites end if put CR&"-"&CR&"Edit the site name..." after tListOfSiteNames if tAreNoSites then ClearSiteData --send "AskToCreateNewSite" to me in 800 millisec put "Add a site..." before tListOfSiteNames set the text of btn "SiteNames" to tListOfSiteNames set the label of btn "SiteNames" to "Add a site..." else set the text of btn "SiteNames" to tListOfSiteNames set the label of btn "SiteNames" to tCurrentSiteName -- just making sure SwitchToSite tCurrentSiteName end if set the uFirstSitePrompt of btn "SiteNames" to "false" -- init set the uNewSite of btn "SiteNames" to "false" -- init set the blendLevel of btn "openPreferences" to 50 put the width of stack kMain div 2 into tCenter put the loc of grp "preferencesGrp" into tStart put tCenter into item 1 of tStart set the loc of grp "preferencesGrp" to tStart show grp "preferencesGrp" put (item 1 of tStart &COMMA& ((the height of grp "preferencesGrp") div 2 -10)) into tFinish move grp "preferencesGrp" from tStart to tFinish in 500 millisec end if end PreferencesSheet --|----------------------------------------------------------------- --| called by PreferencesSheet if it opens and there is no site data --|----------------------------------------------------------------- command AskToCreateNewSite dispatch "mouseUp" to btn "addSite" end AskToCreateNewSite --|----------------------------------------------------------------- --| returns the path to the local preferences file --|----------------------------------------------------------------- function PreferencePath if IsMobile() then return (specialFolderPath("documents") & "/preferences") end if -- if the environment is "development" then -- create any missing sub-folders while we are here if the platform is "MacOS" then put specialFolderPath("Preferences") into tBasicPrefPath end if if the platform is "Win32" then -- why would we develope iOS on Windows? put specialFolderPath(35) into tBasicPrefPath -- C:/Documents and Settings/All Users/Application Data end if -- Try and create an application-specific sub folder put (tBasicPrefPath &SLASH& "Calendar Events") into tPreferencesFolderPath if there is not a folder (tPreferencesFolderPath) then set the defaultFolder to tBasicPrefPath create folder "Calendar Events" put the result into tResult if tResult is not empty then set the defaultFolder to specialFolderPath("resources") answer "Creating the preferences folder" &CR& tBasicPrefPath&SLASH&"Calendar Events" &CR& "resulted in:"&& tResult return tBasicPrefPath & "/preferences" -- use the basic preferences folder end if end if set the defaultFolder to specialFolderPath("resources") -- tidy up -- use the application-specific sub folder return tPreferencesFolderPath & "/preferences" -- end if end PreferencePath command PreferenceRead set the defaultStack to kMain put PreferencePath() into tPreferencePath repeat 10 times -- arbitrary number if there is a file tPreferencePath then exit repeat else -- PreferenceCreateFile PreferenceWrite -- this will create the file with defaults wait 10 millisec with messages end if end repeat open file tPreferencePath for read put the result into tResult if tResult is not empty then answer "I was unable to open the file:"&&tResult end if read from file tPreferencePath until eof put the result into tResult if tResult is not "eof" then answer "I was unable to read the file:"&&tResult end if put it into tPreferenceData -- decrypt the file put fwUnPackModified(tPreferenceData, "americano") into tPreferenceData -- convert the UTF8 file on disk into native LC text put textDecode(tPreferenceData,"UTF8") into tPreferenceData close file tPreferencePath put the result into tResult if tResult is not empty then answer "I was unable to close the file:"&&tResult end if -- do stuff with the preferences settings set the customPropertySet of stack kMain to empty -- default set -- put numToNativeChar(29) into tGS -- Group Separator put numToNativeChar(30) into tUS -- Unit Separator put numToNativeChar(31) into tLS -- Line Separator set the lineDel to CR repeat for each line tLine in tPreferenceData --| a list of the sites and their preference data if word 1 of tline is "" then set the lineDel to tLS set the itemDel to tUS put (word 2 to -2 of tLine) into tSiteSpecifics -- each site is separated by tLS -- data for each site is separated by tUS repeat for each line tSite in tSiteSpecifics repeat for each item tSitePreference in tSite --| Name of site if word 1 of tSitePreference is "" then put (word 2 to -2 of tSitePreference) into tSiteName set the customPropertySet of stack kMain to tSiteName next repeat end if --| Remote URL to Calendar Event Folder if word 1 of tSitePreference is "" then put (word 2 to -2 of tSitePreference) into tRemoteFolderURL set the uURLPathPref of stack kMain to tRemoteFolderURL next repeat end if --| Local Path to Calendar Event Folder if word 1 of tSitePreference is "" then put (word 2 to -2 of tSitePreference) into tLocalFolderPath set the uLocalFolderPath of stack kMain to tLocalFolderPath next repeat end if --| FTP User Name if word 1 of tSitePreference is "" then put (word 2 to -2 of tSitePreference) into tFTP_UserName set the uFTP_UserName of stack kMain to tFTP_UserName next repeat end if --| FTP Password if word 1 of tSitePreference is "" then put (word 2 to -2 of tSitePreference) into tFTP_Password set the uFTP_Password of stack kMain to tFTP_Password next repeat end if --| FTP Server if word 1 of tSitePreference is "" then put (word 2 to -2 of tSitePreference) into tFTP_Server set the uFTP_Server of stack kMain to tFTP_Server next repeat end if --| FTP Path to Calendar Event Folder if word 1 of tSitePreference is "" then put (word 2 to -2 of tSitePreference) into tFTP_FolderPath set the uFTP_FolderPath of stack kMain to tFTP_FolderPath next repeat end if --| Use HTML Entities if word 1 of tSitePreference is "" then put (word 2 to -2 of tSitePreference) into tBtnHilite if tBtnHilite is not among the items "true,false" then put "true" into tBtnHilite -- default end if set the uUseHTML_Entities of stack kMain to tBtnHilite next repeat end if end repeat -- each site's preferences end repeat -- each site set the itemDel to COMMA -- return to expected set the lineDel to CR -- return to expected next repeat end if set the customPropertySet of stack kMain to empty -- return to default set --| Current List of Site Names if word 1 of tline is "" then put (word 2 to -2 of tLine) into tSiteNames replace tLS with CR in tSiteNames put trim(RemoveIncorrectSiteNames(tSiteNames)) into tSiteNames set the uSiteNames of stack kMain to tSiteNames set the text of btn "SiteNames" of stack kMain to (tSiteNames &CR&"-"&CR& "Edit the site name...") next repeat end if --| Current (Active) Site Name if word 1 of tline is "" then put (word 2 to -2 of tLine) into tCurrentSiteName set the uCurrentSiteName of stack kMain to tCurrentSiteName set the label of btn "SiteNames" of stack kMain to tCurrentSiteName -- preference pane set the text of fld "siteName" of stack kMain to tCurrentSiteName -- main workspace next repeat end if --| Open Local button hilite if word 1 of tline is "" then put (word 2 to -2 of tLine) into tBtnHilite if tBtnHilite is not among the items "true,false" then put "true" into tBtnHilite end if set the hilite of btn "openLocal" to tBtnHilite if tBtnHilite then -- set the correct icon dispatch "mouseUp" to btn "openLocal" end if next repeat end if --| Open Remote button hilite if word 1 of tline is "" then put (word 2 to -2 of tLine) into tBtnHilite if tBtnHilite is not among the items "true,false" then put "false" into tBtnHilite end if set the hilite of btn "openRemote" to tBtnHilite if tBtnHilite then -- set the correct icon dispatch "mouseUp" to btn "openRemote" end if next repeat end if --| Publish Local button hilite if word 1 of tline is "" then put (word 2 to -2 of tLine) into tBtnHilite if tBtnHilite is not among the items "true,false" then put "true" into tBtnHilite end if set the hilite of btn "publishLocal" to tBtnHilite if tBtnHilite then -- set the correct icon dispatch "mouseUp" to btn "publishLocal" end if next repeat end if --| Publish Web button hilite if word 1 of tline is "" then put (word 2 to -2 of tLine) into tBtnHilite if tBtnHilite is not among the items "true,false" then put "true" into tBtnHilite end if set the hilite of btn "publishRemote" to tBtnHilite if tBtnHilite then -- set the correct icon dispatch "mouseUp" to btn "publishRemote" end if next repeat end if end repeat ResetPreferences -- this will set the preference pane to match the "current" custom props end PreferenceRead --|----------------------------------------------------------------- --| These are strings that could possibly --| creep into the list of site names --| and we want to make sure that they do not --|----------------------------------------------------------------- function RemoveIncorrectSiteNames tSiteNames filter lines of tSiteNames without empty filter lines of tSiteNames without "-" filter lines of tSiteNames without "Add a site..." filter lines of tSiteNames without "Edit the site name..." filter lines of tSiteNames without "SiteNames" return tSiteNames end RemoveIncorrectSiteNames --|----------------------------------------------------------------- --| --|----------------------------------------------------------------- command PreferenceWrite set the defaultStack to kMain set the customPropertySet of stack kMain to empty -- default set -- put numToNativeChar(29) into tGS -- Group Separator put numToNativeChar(30) into tUS -- Unit Separator put numToNativeChar(31) into tLS -- Line Separator --| List of Site Names put the uSiteNames of stack kMain into tSiteNames -- just being careful put RemoveIncorrectSiteNames(tSiteNames) into tSiteNames replace CR with tLS in tSiteNames -- now we can store the list in our file format put "" && tSiteNames && "" &CR after tPrefData replace tLS with CR in tSiteNames -- now we can step through by line put ""&&SPACE after tPrefData -- the space is required to get a word -- each site is separated by tLS -- data for each site is separated by tUS repeat for each line tSiteName in tSiteNames set the customPropertySet of stack kMain to tSiteName --| Site Name - MUST BE FIRST put "" && tSiteName && "" &tUS after tPrefData --| Remote URL to Calendar Event Folder put "" && (the uURLPathPref of stack kMain) && "" &tUS after tPrefData --| Local Path to Calendar Event Folder put "" && (the uLocalFolderPath of stack kMain) && "" &tUS after tPrefData --| FTP User Name put "" && (the uFTP_UserName of stack kMain) && "" &tUS after tPrefData --| FTP Password put "" && (the uFTP_Password of stack kMain) && "" &tUS after tPrefData --| FTP Server put "" && (the uFTP_Server of stack kMain) && "" &tUS after tPrefData --| FTP Path to Calendar Event Folder put "" && (the uFTP_FolderPath of stack kMain) && "" &tUS after tPrefData --| Use HTML Entities button hilite put "" && (the uUseHTML_Entities of stack kMain) && "" &tUS after tPrefData put tLS after tPrefData -- break for a new site name with data end repeat delete char -1 of tPrefData -- remove trailing tLS put SPACE&"" after tPrefData set the customPropertySet of stack kMain to empty -- default set --| Current / Active Site Name - Must come AFTER put (the label of btn "SiteNames" of stack kMain) into tCurrentSiteName put RemoveIncorrectSiteNames(tCurrentSiteName) into tCurrentSiteName put CR&"" && tCurrentSiteName && "" after tPrefData set the uCurrentSiteName of stack kMain to tCurrentSiteName --| Open Local button hilite put CR&"" && (the hilite of btn "openLocal") && "" after tPrefData --| Open Remote button hilite put CR&"" && (the hilite of btn "openRemote") && "" after tPrefData --| Publish Local button hilite put CR&"" && (the hilite of btn "publishLocal") && "" after tPrefData --| Publish Web button hilite put CR&"" && (the hilite of btn "publishRemote") && "" after tPrefData put PreferencePath() into tPreferencePath put textEncode(tPrefData,"UTF-8") into tPrefData put fwPackModified(tPrefData, "americano", "NotThatSecretCode") into URL ("file:"&tPreferencePath) -- file is saved as UTF8 put the result into tResult if (tResult is not empty) AND (IsDev() OR IsBeta()) then -- err answer "Writing the preference file failed with:" &CR& the result exit PreferenceWrite end if StorePreferencesInCustomProps tCurrentSiteName set the uNewSite of btn "SiteNames" of stack kMain to "false" end PreferenceWrite --|----------------------------------------------------------------- --| Stores the current field contents of the preference pane into --| a customProperty set that is named for the label of btn SiteNames --| --|----------------------------------------------------------------- command StorePreferencesInCustomProps pSiteName set the customPropertySet of stack kMain to empty -- default set if pSiteName is empty then put the label of btn "SiteNames" of stack kMain into pSiteName end if put RemoveIncorrectSiteNames(pSiteName) into pSiteName set the uCurrentSiteName of stack kMain to pSiteName put line 1 to -3 of the text of btn "SiteNames" into tSiteNames -- remove horizontal rule and editing menu item put RemoveIncorrectSiteNames(tSiteNames) into tSiteNames set the uSiteNames of stack kMain to tSiteNames set the customPropertySet of stack kMain to pSiteName set the uURLPathPref of stack kMain to fld "URLPathPref" set the uLocalFolderPath of stack kMain to fld "localPathPref" set the uFTP_UserName of stack kMain to fld "ftpUserNamePref" set the uFTP_Password of stack kMain to fld "ftpPasswordPref" set the uFTP_Server of stack kMain to fld "ftpServerPref" set the uFTP_FolderPath of stack kMain to fld "ftpDirectoryPathPref" set the uUseHTML_Entities of stack kMain to the hilite of btn "UseHTMLEntities" set the customPropertySet of stack kMain to empty -- return to default set end StorePreferencesInCustomProps --|----------------------------------------------------------------- --| puts the values of the originally-current site back into the fields --| I've changed the way that "Save" works... --| now it only saves the content of the site --| the Current site is set as soon as the OptionMenu selection is made --| For now I'm leaving the redundant code in --|----------------------------------------------------------------- command ResetPreferences set the customPropertySet of stack kMain to empty -- default set set the uNewSite of btn "SiteNames" of stack kMain to "false" put the uCurrentSiteName of stack kMain into tSiteName put RemoveIncorrectSiteNames(tSiteName) into tSiteName -- if tSiteName is empty then -- answer "ResetPreferences issue where tSiteName is now empty" -- breakpoint -- end if SwitchToSite tSiteName -- make this one the currently active site end ResetPreferences --|----------------------------------------------------------------- --| remove the user settings from a site --|----------------------------------------------------------------- command ClearSiteData put empty into fld "URLPathPref" put empty into fld "localPathPref" put empty into fld "ftpUserNamePref" put empty into fld "ftpPasswordPref" put empty into fld "ftpServerPref" put empty into fld "ftpDirectoryPathPref" set the hilite of btn "UseHTMLEntities" to "true" -- default end ClearSiteData --|----------------------------------------------------------------- --| fills in the preference fields to match the site --| (does not set the label of the "siteNames" option menu) --|----------------------------------------------------------------- command SwitchToSite pSiteName --set the customPropertySet of stack kMain to empty -- default set put the customPropertySets of stack kMain into tCustomPropSets if (pSiteName is empty) OR (pSiteName is "-") OR (pSiteName is "Edit this site...") OR\ (pSiteName is "Add a site...") OR (pSiteName is "SiteNames") then exit SwitchToSite end if set the customPropertySet of stack kMain to pSiteName put the uURLPathPref of stack kMain into fld "URLPathPref" put the uLocalFolderPath of stack kMain into fld "localPathPref" put the uFTP_UserName of stack kMain into fld "ftpUserNamePref" put the uFTP_Password of stack kMain into fld "ftpPasswordPref" put the uFTP_Server of stack kMain into fld "ftpServerPref" put the uFTP_FolderPath of stack kMain into fld "ftpDirectoryPathPref" put the uUseHTML_Entities of stack kMain into tBtnHilite if tBtnHilite is not among the items of "true,false" then put "true" into tBtnHilite set the uUseHTML_Entities of stack kMain to tBtnHilite end if set the hilite of btn "UseHTMLEntities" to tBtnHilite set the customPropertySet of stack kMain to empty -- return to default set end SwitchToSite command ChangeSiteName pOriginalSiteName pNewSiteName -- change the name of the customPropertySet --> set the defaultStack to kMain set the customPropertySet of stack kMain to empty set the customPropertySet of stack kMain to pOriginalSiteName put the customKeys of stack kMain into tCustomKeys -- create a new customPropertySet -- set the customPropertySet of stack kMain to pNewSiteName -- set the customKeys of stack kMain to tCustomKeys -- all the keys are empty -- copy over the customKey data from the old set to the new one if pOriginalSiteName = pNewSiteName then -- ie: "name" = "NaMe" -- LiveCode will treat these 2 as the same customPropertySets -- so we need to start by renaming the first CustomPropSet to something unique -- which really means . . . -- completely remove the first customPropertySet -- and replace it with -- an intermediary customPropertySet named "elsoTempPlaceHolderPropSet555" -- before trying to switch to the new (same name) customPropertySet -- insert the intermediary customPropertySet repeat for each line tKey in tCustomKeys set the customPropertySet of stack kMain to pOriginalSiteName put the tKey of stack kMain into tKeyData set the customPropertySet of stack kMain to "elsoTempPlaceHolderPropSet555" set the tKey of stack kMain to tKeyData end repeat -- delete the original customPropertySet put (the customPropertySets of stack kMain) into tCustomPropertySets filter tCustomPropertySets without pOriginalSiteName -- remove the old list name set the customPropertySets of stack kMain to tCustomPropertySets -- now insert the Same-Named customPropertySet since -- there won't be a name-space conflict anymore repeat for each line tKey in tCustomKeys set the customPropertySet of stack kMain to "elsoTempPlaceHolderPropSet555" put the tKey of stack kMain into tKeyData set the customPropertySet of stack kMain to pNewSiteName set the tKey of stack kMain to tKeyData end repeat else -- there isn't a Same-Name problem, just copy from old to new repeat for each line tKey in tCustomKeys set the customPropertySet of stack kMain to pOriginalSiteName put the tKey of stack kMain into tKeyData set the customPropertySet of stack kMain to pNewSiteName set the tKey of stack kMain to tKeyData end repeat end if -- delete the old customPropertySet put (the customPropertySets of stack kMain) into tCustomPropertySets filter tCustomPropertySets without pOriginalSiteName -- remove the old list name if pOriginalSiteName = pNewSiteName then -- filter tCustomPropertySets without "elsoTempPlaceHolderPropSet555" -- don't remove this line if the name is equivalent... ie: "name" = "NaMe" put pNewSiteName & CR before tCustomPropertySets end if -- alphabetize the list sort lines of tCustomPropertySets ascending -- filter tCustomPropertySets without "cREVGeneral" -- remove the "cREVGeneral" set put RemoveIncorrectSiteNames(tCustomPropertySets) into tCustomPropertySets -- filter tCustomPropertySets without empty -- remove any empty lines put trim(tCustomPropertySets) into tCustomPropertySets set the customPropertySets of stack kMain to tCustomPropertySets set the defaultStack to kMain -- update the list of siteNames put the uSiteNames of stack kMain into tSiteNameList -- replace SPACE with "_" in tSiteNameList -- get an exact match repeat with x = 1 to (the number of lines in tSiteNameList) if pOriginalSiteName is line x of tSiteNameList then put pNewSiteName into line x of tSiteNameList exit repeat end if end repeat set the uSiteNames of stack kMain to tSiteNameList -- update the name of the current site name if needed if the uCurrentSiteName of stack kMain is pOriginalSiteName then set the uCurrentSiteName of stack kMain to pNewSiteName end if end ChangeSiteName command DeleteSite pSiteName set the customPropertySet of stack kMain to empty if char 1 to 4 of pSiteName is not "cREV" then put the customPropertySets of stack kMain into tCustomPropertySets filter lines of tCustomPropertySets without pSiteName set the customPropertySets of stack kMain to tCustomPropertySets set the customPropertySet of stack kMain to empty end if put the uSiteNames of stack kMain into tSiteNames filter lines of tSiteNames without pSiteName put RemoveIncorrectSiteNames(tSiteNames) into tSiteNames set the uSiteNames of stack kMain to tSiteNames if the uCurrentSiteName of stack kMain is pSiteName then set the uCurrentSiteName of stack kMain to empty end if end DeleteSite --|----------------------------------------------------------------- --| Open the 91ֱ website --| to the Calendar Events resource page --| option to open other URL as a parameter --|----------------------------------------------------------------- command GoURL pURL if (pURL begins with "http://") OR (pURL begins with "https://") then launch URL pURL else launch url "/livecode/calendarevents.html" end if end GoURL --|----------------------------------------------------------------- --| NetworkType & GetNetworkType are thanks to: --| Ralph DiMola --| IT Director --| Evergreen Information Services --| rdimola@evergreeninfo.net --|----------------------------------------------------------------- command NetworkType if GetNetworkType() = "libURL" then answer"tsNet is disabled(LibURL)" else answer "tsNet in use"&cr&"Version==>"& tsNetVersion() end if end NetworkType function GetNetworkType local tLibUrlDriver try put the behavior of stack"revLibUrl" into tLibUrlDriver end try if tLibUrlDriver is empty then return "libURL" else return "tsNet" end if end GetNetworkType --|----------------------------------------------------------------- --| Old-School encryption method from Fourth World --| requires the Zip library --| --| updated 4-24-2021 --| now uses numToNativeChar() and nativeCharToNum() --| rather than the depricated pre-version 7 form --| numToChar() and charToNum() --| --| RevZip plugin IS used --| (At one point we removed the Compression stage since the RevZip.bundle --| in LC ver 9.6.2 was getting munged by the code signing process.) --|----------------------------------------------------------------- function fwPackModified pData, pPassword, pEncryptionMethod --pEncryptionMethod is depricated here -- local tMd5, tRandSeed, tMax, tOffsetsA -- local tStackName, tSaveVis, tFile -- if pPassword is empty then -- Simply mark the data as not password-protected -- and compress it without further modification: try put "00"&compress(pData) into pData --RevZip inclusion needed -- put "00"&pData into pData -- used this line when RevZip was temporarily removed catch tErr if IsDevOrBeta() then answer "fwPackModified error:" && tErr end if end try else -- Get MD5 digest: put md5digest(pPassword) into tKeyString put len(tKeyString) into tKeyStringLen -- put compress(pData) into pData --RevZip inclusion needed -- -- Apply it with Xor to the data one byte at a time: put 0 into i put empty into tCryptoText repeat for each char k in pData add 1 to i if i > tKeyStringLen then put 1 into i put char i of tKeyString into tKeyChar put numToNativeChar(nativeCharToNum(k) bitxor nativeCharToNum(tKeyChar)) after tCryptoText end repeat -- -- Mark the data as password-protected: put "01" & tCryptoText into pData end if -- Convert to common low ASCII: return base64encode(pData) end fwPackModified --|----------------------------------------------------------------- --| Old-School de-encryption method from Fourth World --| requires the Zip library --| --| updated 4-24-2021 --| now uses numToNativeChar() and nativeCharToNum() --| rather than the depricated pre-version 7 form --| numToChar() and charToNum() --|----------------------------------------------------------------- function fwUnPackModified pData, pPassword -- local tMd5, tRandSeed, tMax, tOffsetsA -- -- Convert from base64 back to binary: put base64decode(pData) into pData -- Check and remove password-protection flag: put char 1 to 2 of pData into tEncryptionMethod delete char 1 to 2 of pData -- switch tEncryptionMethod case "00" --no encryption break -- case "01" -- mdx -- Get MD5 digest: put md5digest(pPassword) into tKeyString put len(tKeyString) into tKeyStringLen -- -- Apply it with Xor to the data one byte at a time: put 0 into i put empty into tClearText repeat for each char k in pData add 1 to i if i > tKeyStringLen then put 1 into i put char i of tKeyString into tKeyChar put numToNativeChar(nativeCharToNum(k) bitxor nativeCharToNum(tKeyChar)) after tClearText end repeat put tClearText into pData break -- end switch -- -- Attempt to decompress data, throwing an error if not valid: try get decompress(pData) --RevZip inclusion needed catch tErr -- throw "Wrong password" -- exit to top if IsDevOrBeta() then answer "fwUnPackModified error:" && tErr end if finally return it end try end fwUnPackModified  ~dCalendar Events 2/27/21 -- sanitize replace TAB with empty in tRawDate replace CR with empty in tRawDate set the itemDel to SLASH put "20" & item -1 of tRawDate into tYear put item 2 of tRawDate into tDay put item 1 of tRawDate into tMonth put (tYear &"-"& tMonth &"-"& tDay) into tWidgetDate ask information "Enter a date" with tWidgetDate titled "Custom Date" put it into tDate if (tDate is "Cancel") OR (tDate is empty) then exit mouseDoubleUp end if put empty into tFormatErr -- init set the itemDel to "-" -- parse the user's date format for glaring errors if the number of items of tDate is not 3 then put "error,You must indicate: Year-Month-Day." into tFormatErr end if if (item 1 of tDate is not a number) OR (item 2 of tDate is not a number) OR (item 3 of tDate is not a number) then put "error,The date items must all be numbers." into tFormatErr end if if ((the num of chars of item 1 of tDate is not 2) AND (the num of chars of item 1 of tDate is not 4)) OR\ (char -1 of tDate is not a number) OR (char 1 of tDate is not a number) then put "error,The year is not formatted correctly." into tFormatErr end if if (item 2 of tDate < 1) OR (item 2 of tDate > 12) then put "error,The month number is out of range." into tFormatErr end if if (item 3 of tDate < 1) OR (item 3 of tDate > 31) then put "error,The day number is out of range." into tFormatErr end if set the itemDel to COMMA -- tidy if item 1 of tFormatErr is "error" then answer item 2 of tFormatErr exit mouseDoubleUp end if -- just a little fun put item 1 of tDate into tUserYear if the num of chars of tUserYear is 2 then if tUserYear < 60 then put 20&tUserYear into tUserYear end if end if if (tUserYear > tYear + 6) then answer tUserYear&"... Wow! You are psychic." with "I Am" titled "Rasputin" end if put WidgetDateToEnglishDate(tDate) into tDate put tDate into fld "eventDate" send "textChanged" to me end mouseDoubleUp    cREVGeneral  revUniqueID/QwBtablefalsescripteditorselection474scripteditorvscroll cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' eventDateLabel ^^^^^^e~  cREVGeneral  revUniqueID/QwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Event Date ''] eventTimeGrp i8on openField show grp "numberPadGrp" end openField  GT  cREVGeneral  revUniqueIDOwBscripteditorvscrollscripteditorselection12 eventTimeLabel ^^^^^^eThis appears in the "Day-Box"d  cREVGeneral  revUniqueID4wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Event Time '  eventTime(9 c  cREVGeneral  revUniqueID4wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' '\eventTimeHyphenP?% Y  cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueID4wBtablefalse  - ' 'ZeventTimeStart+h%IThis appears in the "Day-Box" :  cREVGeneral  revUniqueID4wBtablefalsescripteditorselection0scripteditorvscroll cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' '[eventTimeFinish+hO%IThis appears in the "Day-Box" :  cREVGeneral  revUniqueID4wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' _repeatingEventGrpamG  cREVGeneral  revUniqueIDQwBscripteditorvscrollscripteditorselection0 b startDate)x0on mouseDoubleUp put the date into tRawDate --> 2/27/21 set the itemDel to SLASH put "20" & item -1 of tRawDate into tYear put item 2 of tRawDate into tDay put item 1 of tRawDate into tMonth put (tYear &"-"& tMonth &"-"& tDay) into tWidgetDate ask information "Enter a date" with tWidgetDate titled "Custom Date" put it into tDate if (tDate is "Cancel") OR (tDate is empty) then exit mouseDoubleUp end if put empty into tFormatErr -- init set the itemDel to "-" -- parse the user's date format for glaring errors if the number of items of tDate is not 3 then put "error,You must indicate: Year-Month-Day." into tFormatErr end if if (item 1 of tDate is not a number) OR (item 2 of tDate is not a number) OR (item 3 of tDate is not a number) then put "error,The date items must all be numbers." into tFormatErr end if if (the num of chars of item 1 of tDate is not 2) AND (the num of chars of item 1 of tDate is not 4) then put "error,The year is not formatted correctly." into tFormatErr end if put item 1 of tDate into tUserYear if the num of chars of tUserYear is 2 then if tUserYear < 60 then put 20&tUserYear into tUserYear end if end if if (tUserYear > tYear + 6) then answer tUserYear&"... Wow! You are psychic." with "I Am" titled "Rasputin" end if set the itemDel to COMMA -- tidy if item 1 of tFormatErr is "error" then answer item 2 of tFormatErr exit mouseDoubleUp end if put tDate into fld "eventDate" end mouseDoubleUp    cREVGeneral  revUniqueID/QwBtablefalsescripteditorselection1326scripteditorvscroll cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' cstartDateLabel ^^^^^^eg  cREVGeneral  revUniqueID/QwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Start Date ' dendDate9x0on mouseDoubleUp put the date into tRawDate --> 2/27/21 set the itemDel to SLASH put "20" & item -1 of tRawDate into tYear put item 2 of tRawDate into tDay put item 1 of tRawDate into tMonth put (tYear &"-"& tMonth &"-"& tDay) into tWidgetDate ask information "Enter a date" with tWidgetDate titled "Custom Date" put it into tDate if (tDate is "Cancel") OR (tDate is empty) then exit mouseDoubleUp end if put empty into tFormatErr -- init set the itemDel to "-" -- parse the user's date format for glaring errors if the number of items of tDate is not 3 then put "error,You must indicate: Year-Month-Day." into tFormatErr end if if (item 1 of tDate is not a number) OR (item 2 of tDate is not a number) OR (item 3 of tDate is not a number) then put "error,The date items must all be numbers." into tFormatErr end if if (the num of chars of item 1 of tDate is not 2) AND (the num of chars of item 1 of tDate is not 4) then put "error,The year is not formatted correctly." into tFormatErr end if put item 1 of tDate into tUserYear if the num of chars of tUserYear is 2 then if tUserYear < 60 then put 20&tUserYear into tUserYear end if end if if (tUserYear > tYear + 6) then answer tUserYear&"... Wow! You are psychic." with "I Am" titled "Rasputin" end if set the itemDel to COMMA -- tidy if item 1 of tFormatErr is "error" then answer item 2 of tFormatErr exit mouseDoubleUp end if put tDate into fld "eventDate" end mouseDoubleUp    cREVGeneral  revUniqueIDRwBtablefalsescripteditorselection1326scripteditorvscroll cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' e endDateLabel ^^^^^^ e Leave empty to continue all year2a  cREVGeneral  revUniqueIDRwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse End Date ' fdays)xon mouseUp lock screen hide grp "numberPadGrp" show grp "repeatingDayTypeGrp" hide widget "Calendar" unlock screen end mouseUp on mouseRelease --hide grp "numberPadGrp" select empty end mouseRelease on mouseDoubleUp put the date into tRawDate --> 2/27/21 set the itemDel to SLASH put "20" & item -1 of tRawDate into tYear put item 2 of tRawDate into tDay put item 1 of tRawDate into tMonth put (tYear &"-"& tMonth &"-"& tDay) into tWidgetDate ask information "Enter a date" with tWidgetDate titled "Custom Date" put it into tDate if (tDate is "Cancel") OR (tDate is empty) then exit mouseDoubleUp end if put empty into tFormatErr -- init set the itemDel to "-" -- parse the user's date format for glaring errors if the number of items of tDate is not 3 then put "error,You must indicate: Year-Month-Day." into tFormatErr end if if (item 1 of tDate is not a number) OR (item 2 of tDate is not a number) OR (item 3 of tDate is not a number) then put "error,The date items must all be numbers." into tFormatErr end if if (the num of chars of item 1 of tDate is not 2) AND (the num of chars of item 1 of tDate is not 4) then put "error,The year is not formatted correctly." into tFormatErr end if put item 1 of tDate into tUserYear if the num of chars of tUserYear is 2 then if tUserYear < 60 then put 20&tUserYear into tUserYear end if end if if (tUserYear > tYear + 6) then answer tUserYear&"... Wow! You are psychic." with "I Am" titled "Rasputin" end if set the itemDel to COMMA -- tidy if item 1 of tFormatErr is "error" then answer item 2 of tFormatErr exit mouseDoubleUp end if put tDate into fld "eventDate" end mouseDoubleUp MClick the calendar day-names   cREVGeneral  revUniqueIDHRwBtablefalsescripteditorselection129scripteditorvscroll cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' g daysLabel ^^^^^^8c  cREVGeneral  revUniqueIDЧHRwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Repeating Days 's startDateb@ `on mouseUp CalendarTargetFocus (the short name of me) hide grp "numberPadGrp" end mouseUp &&@b  cREVGeneral  revUniqueID`Y`wBscripteditorvscrollscripteditorselection33  tendDateb@ `on mouseUp CalendarTargetFocus (the short name of me) hide grp "numberPadGrp" end mouseUp &&@ Leave empty to continue all yeard  cREVGeneral  revUniqueIDY`wBscripteditorvscrollscripteditorselection95  exceptionsLabel ^^^^^^qCancel these individual daysA  cREVGeneral  revUniqueID &ywBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Exception Days ' exceptionMenue+uon menuPick pChosenItem -- set the calendar widget to show this date put pChosenItem into tDate -- Sat, Mar 6, 2021 put word 2 of item 2 of tDate into tDayNum -- 6 delete word 2 of item 2 of tDate -- Sat, Mar , 2021 replace SPACE with empty in tDate -- Sat,Mar,2021 put item 3 of tDate into tYear put item 2 of tDate into tAbbreviatedMonthName -- Mar put 0+0 into tMonthNum -- init repeat for each line tMonth in the monthNames add 1 to tMonthNum if tAbbreviatedMonthName is in tMonth then exit repeat end if end repeat put (tYear &"-"& tMonthNum &"-"& tDayNum) into tWidgetDate set the selectedDate of widget "Calendar" to tWidgetDate end menuPick on mouseUp hide grp "numberPadGrp" end mouseUp Cancel these individual days   cREVGeneral  revUniqueID0&ywBscripteditorvscrollscripteditorselection764 [oplusExceptionGrp iyn  cREVGeneral  revUniqueID0lAwB[pexceptionPlusBorderKA}"   profile cREVGeneral  revUniqueIDlAwB[q addExceptionbe -- simulate a button hilite exceptionPlusBorder on mouseDown lock screen hide grp "numberPadGrp" -- create the toggle pill hilite set the innerShadow["angle"] of grc "exceptionPlusBorder" to 90 set the innerShadow["opacity"] of grc "exceptionPlusBorder" to 120 set the innerShadow["size"] of grc "exceptionPlusBorder" to 1 set the innerShadow["distance"] of grc "exceptionPlusBorder" to 2 set the backgroundColor of grc "exceptionPlusBorder" to 210,210,210 unlock screen end mouseDown on mouseUp CalendarTargetFocus (the short name of me) set the innerShadow of grc "exceptionPlusBorder" to empty set the backgroundColor of grc "exceptionPlusBorder" to 235,235,235 end mouseUp on mouseRelease hide grp "numberPadGrp" set the innerShadow of grc "exceptionPlusBorder" to empty set the backgroundColor of grc "exceptionPlusBorder" to 235,235,235 select empty end mouseRelease ~,Add an exception to cancel a specific date. e+  cREVGeneral  revUniqueIDAwBscripteditorvscrollscripteditorselection731 [rminusExceptionGrp ij  cREVGeneral  revUniqueID@lAwB[sexceptionMinusBorderKA" " profile cREVGeneral  revUniqueIDlAwB[tdeleteExceptionbe  -- simulate a button hilite on mouseDown lock screen hide grp "numberPadGrp" -- create the toggle pill hilite set the foregroundColor of grc "exceptionMidLine" to 120,120,120 set the dropShadow["angle"] of grc "exceptionMidLine" to 90 set the dropShadow["opacity"] of grc "exceptionMidLine" to 140 set the dropShadow["size"] of grc "exceptionMidLine" to 5 set the dropShadow["distance"] of grc "exceptionMidLine" to 2 set the innerShadow["angle"] of grc "exceptionMinusBorder" to 90 set the innerShadow["opacity"] of grc "exceptionMinusBorder" to 140 set the innerShadow["size"] of grc "exceptionMinusBorder" to 2 set the innerShadow["distance"] of grc "exceptionMinusBorder" to 1 set the backgroundColor of grc "exceptionMinusBorder" to 210,210,210 unlock screen end mouseDown on mouseUp put the label of btn "exceptionMenu" into tExceptionToDelete put the text of btn "exceptionMenu" into tExceptionList filter tExceptionList without tExceptionToDelete set the text of btn "exceptionMenu" to tExceptionList set the label of btn "exceptionMenu" to (line 1 of tExceptionList) set the foregroundColor of grc "exceptionMidLine" to 170,170,170 set the dropShadow of grc "exceptionMidLine" to empty set the innerShadow of grc "exceptionMinusBorder" to empty set the backgroundColor of grc "exceptionMinusBorder" to 235,235,235 end mouseUp on mouseRelease hide grp "numberPadGrp" set the foregroundColor of grc "exceptionMidLine" to 170,170,170 set the dropShadow of grc "exceptionMidLine" to empty set the innerShadow of grc "exceptionMinusBorder" to empty set the backgroundColor of grc "exceptionMinusBorder" to 235,235,235 select empty end mouseRelease Remove an exception date a-  cREVGeneral  revUniqueIDAwBscripteditorvscrollscripteditorselection119 [uexceptionMidLineKd  cREVGeneral  revUniqueIDPlAwBinOutGrp i` P7u  cREVGeneral  revUniqueIDq~wBreadFile'p local sAutoRetryFlag -- if OpenLocalFile can't find the temp file and we send mouseUp again - ineffective :-/ local sRetryCounter -- counts the number of times that we retry the mouseUp handler local sYear -- so we know what year we are working with even if the remote file (and temp file) is empty local sFilePath local sLocalPath -- DEPRICATED... where the remote file would go if it were saved locally (DEPRICATED?) local sMaxRequests local sActiveRequests local sDownloadArray local sRequestedArray local sDataDir -- a temp folder that the remote data is saved to (tsNet) local sDownloadErrors constant kMain = "Calendar Events" --|----------------------------------------------------------------- --| Opening a file from the web requires the inclusions of: --| tsNet, mergJSON --|----------------------------------------------------------------- on mouseUp -- get a filepath select empty -- in case we are auto-running the whole thing again because the temp file didn't get written -- see OpenLocalFile if not sAutoRetryFlag then put empty into sYear -- init put 0 into sRetryCounter -- init end if put empty into sAutoRetryFlag -- init if the hilite of btn "openLocal" then put fld "localPathPref" into tFolderPath put "true" into tLocalPathIsGood if there is not a folder tFolderPath then put "false" into tLocalPathIsGood put specialFolderPath("desktop") into tFolderPath end if answer file "Select a text file" with tFolderPath with type "Text|txt" if it is not empty then put it into sFilePath else exit mouseUp end if if not tLocalPathIsGood then answer "Should I save this folder as the local preference?" with "No" OR "Save" if it is "Save" then set the itemDel to SLASH put (item 1 to -2 of sFilePath) into fld "localPathPref" set the itemDel to COMMA -- tidy end if end if OpenLocalFile else -- retrieve data from server by downloading to a local temp file -- this method uses an array -- so that it could download multiple files in the background -- we only need it to get one at a time but we will keep this -- array file structure -- first, find out if we have remote URL information -- if we don't, there is no point in continuing -- get the remote directory path from our Preferences pane put fld "URLPathPref" into tRemoteDir if tRemoteDir is empty then answer warning "No path to server."&CR& "Check the preferences." send "PreferencesSheet" to stack kMain in 500 millisec exit mouseUp end if if char -1 of tRemoteDir is not SLASH then put SLASH after tRemoteDir end if tsNetInit -- required before using any tsNet features set the customPropertySet of stack kMain to empty -- we will only show this field if there is an error put empty into fld "Server Response" -- init -- Make sure we are not already downloading files in the background if sActiveRequests is not 0 then answer "Downloads currently in progress." end if if sYear is empty then -- first make sure that the file exists -- if it doesn't we can report this to the user -- The following line is the call to tsNetHeadSync ask "What year?" with (word -1 of the long date) as sheet if the result is "cancel" then exit mouseUp end if put trim(it) into tYear if tYear is empty then exit mouseUp end if if (tYear is not a number) OR ((the number of chars of tYear is not 2) AND (the number of chars of tYear is not 4)) then answer "'"& tYear &"'" && "is not a valid year for this calendar tool." exit mouseUp end if RetryProgress "start" -- show the user an indeterminate progress indicator if the num of chars of tYear is 2 then if tYear > 70 then put "19"&tYear into tYear else put "20"&tYear into tYear end if end if else -- sYear is not empty put sYear into tYear end if put tYear&"_calendar_events.txt" into tFileName put tYear into sYear -- build the full URL -- ----------------------------------------------------------------- -- -- this is where the same local file would be -- put fld "localPathPref" into tLocalDir -- if tLocalDir is empty then -- -- just wing it with a path to the desktop -- put specialFolderPath("desktop") into tLocalDir -- end if -- if char -1 of tLocalDir is SLASH then -- delete char -1 of tLocalDir -- end if -- put (tLocalDir &SLASH& tFileName) into sLocalPath -- ----------------------------------------------------------------- set the uRemoteSourceFile of stack kMain to "true" put (sDataDir & SLASH & tFileName) into sFilePath -- used by OpenLocalFile put (tRemoteDir & tFileName) into tRemoteFilePath -- now see if that file exists put tsNetHeadSync(tRemoteFilePath, tHeaders, tResult, tBytes) into tRecvHeaders if tResult begins with "tsneterr:" then put ("When attempting to see if the file" &CR& tFileName &CR& "existed, tsNet reported an error:"&CR&CR& tResult) into field "Server Response" show fld "Server Response" exit mouseUp else if "200 OK" is not in tRecvHeaders then -- problem of some sort if "404 Not Found" is in tRecvHeaders then answer "I'm sorry but I can't find the file:" &CR&CR& tRemoteFilePath RetryProgress "stop" exit mouseUp end if put "HTTP response code is:" && tResult & cr & cr into field "Server Response" put "HTTP returned headers are:" && cr & tRecvHeaders after field "Server Response" show fld "Server Response" exit mouseUp end if end if -- the file is accessible -- headers reported: HTTP/1.1 200 OK -- Set a limit for the number of requests we want to perform at a time put 3 into sMaxRequests -- would 1 be better? -- This will be set to true if an error occurs while downloading any of the files put "false" into sDownloadErrors -- Reset the array of requested downloads put empty into sRequestedArray -- Specify the folder that downloaded files will be stored in put specialFolderPath("temporary")&SLASH&"elsoCalendarEvents" into sDataDir -- make sure that the temp folder we will use exists if there is not a folder sDataDir then create folder sDataDir put the result into tResult if tResult is not empty then answer warning "Download Failed. The temporary 'elsoCalendarEvents' directory could not be created." with "Understood" exit mouseUp end if end if -- Retrieve the JSON data file which contains the files to download and the current download status of each -- and convert it to an array called sDownloadArray --put URL("file:" & sDataDir & "/URLs.json") into tJsonData ------------------------------------------------------------ -- The JSON data file must be in the following format: ------------------------------------------------------------ -- [ -- { -- "URL": "http://traditionaltaekwondo.org/calendar/2021_calendar_events.txt", -- "status": "", -- "filename": "2021_calendar_events.txt" -- }, -- { -- "URL": "", -- "status": "", -- "filename": "" -- } -- ] ------------------------------------------------------------ put the uJsonFileFormat of stack kMain into tJsonData put (QUOTE&"URL""E&":" &"E& tRemoteFilePath "E&COMMA) into line 3 of tJsonData put (QUOTE& "filename" "E& ":" &"E& tFileName "E) into line 5 of tJsonData -- JSONToArray() requires the inclusion of mergJSON put JSONToArray(tJsonData) into sDownloadArray put "Downloading..."&CR into fld "Server Response" -- Begin downloading send "downloadURLs" to me in 0 milliseconds end if end mouseUp -- we have downloaded the remote file(s) to a temporary folder -- the local filepath is in a script local variable: sTempFilePath command OpenLocalFile -- error checking if there is not a file sFilePath then -- sometimes the file doesn't load until the button has been clicked a second time -- this little "retry" seems to solve it for now if sRetryCounter < 5 then add 1 to sRetryCounter put "true" into sAutoRetryFlag send "mouseUp" to me in 100 millisec exit OpenLocalFile end if put 0 into sRetryCounter -- init wait 1500 millisec with messages -- let the visual progress display more than a flash RetryProgress "stop" put empty into sAutoRetryFlag -- init answer "I was expecting to find a local temp file that was copied from the server... but I don't see it. Trying a second time often works." with "Ok" exit OpenLocalFile end if RetryProgress "stop" put empty into sAutoRetryFlag -- init put URL ("file:" & sFilePath) into tTextToDecode put textDecode(tTextToDecode,"UTF-8") into tEventList put the result into tResult if tResult is not empty then answer "After writing the data from the server to a temp file, reading the temp file failed with error:"&&tResult end if if the hilite of btn "openRemote" then -- tidy up by deleting the temp file delete file sFilePath -- deleting this file might keep us from reading old data put the result into tResult if tResult is not empty then answer "Deleting the temp file failed with error:"&&tResult end if end if replace TAB with empty in tEventList -- just being careful -- store the raw file as emergency backup set the uEventList of stack kMain to tEventList -- put numToNativeChar(29) into tGS -- Group Separator put numToNativeChar(30) into tUS -- Unit Separator -- put numToNativeChar(31) into tLS -- Line Separator set the itemDel to tUS put 1 into tLineNum -- init -- US US US US US -- use the templateField to take HTMLEntities and turn them into proper glyphs -- (we assume that there will be no "special characters" to process in the date) put empty into tDataGridEventString -- init repeat for each line tEvent in tEventList put item 4 of tEvent into tEventTitle set the HTMLText of the templateField to tEventTitle put the text of the templateField into tEventTitle -- Make the date look pretty for the data grid UI if (char 1 of item 2 of tEvent is "W") OR (char 1 of item 2 of tEvent is "M")then -- repeating event put char 1 of item 2 of tEvent into tRepeatType put item 2 of tEvent into tRepeatingEvent set the itemDel to "|" put item 6 of tRepeatingEvent into tDayNums if tRepeatType is "M" then -- monthly set the itemDel to "." put item 1 of tDayNums into tWeekNumber put item 2 of tDayNums into tDayNum put WeekNumsToOrdinals(tWeekNumber) && DayNumsToShortNames(tDayNum) into tEventDate set the itemDel to "|" -- expected end if if tRepeatType is "W" then -- weekly replace "." with COMMA in tDayNums -- switch to item delimiter that DayNumsToNames() expects -- for repeating events -- we will show the day names is the date column put DayNumsToShortNames(tDayNums) into tEventDate end if set the itemDel to tUS -- return it to what it was else -- single event... will actually be a month put item 2 of tEvent into tMonthNum put item 3 of tEvent into tDayNum put line tMonthNum of the abbreviated english monthNames into tMonthName put (tMonthName&"."&&tDayNum) into tEventDate end if -- Make the description put item 6 of tEvent into tEventDescription set the HTMLText of the templateField to tEventDescription put the text of the templateField into tEventDescription -- -- any Line Separators were coverted to HTML entities -- -- so put back the Line Separators -- replace "" with tLS in tEventDescription -- depricated put (tEventTitle &TAB& tEventDate &TAB& tEventDescription &TAB& tEvent &CR) after tDataGridEventString end repeat delete char -1 of tDataGridEventString -- remove trailing CR set the dgText of group "EventDataGrid" to tDataGridEventString -- we label the year looking into the file data -- not by reading the file name put item 1 of tEventList into tYear if tYear is empty then put sYear into tYear end if put tYear into fld "eventListYear" ClearItOut "true" FileState "clean" set the uRemoteSourceFile of stack kMain to "false" -- the file is not from a remote source enable group "EventDataGrid" dispatch "mouseUp" to btn "clear" -- reset other objects dispatch "mouseUp" to btn "publishLocal" -- set the icon on the "publish" btn --> just being careful -- set the icon of btn "writeFile" to 1025 set the itemDel to COMMA -- tidy DeleteEventMenuItemEnableDisable "disable" end OpenLocalFile on downloadURLs put the number of lines in the keys of sDownloadArray into tArrayCount -- If we are not already downloading the maximum number of simultaneous files, loop through -- sDownloadArray and initiate a request to an entry that is not yet "downloaded" if sActiveRequests < sMaxRequests then repeat with x = 1 to tArrayCount if sDownloadArray[x]["status"] is not "downloaded" and sRequestedArray[x] is not "true" then put tsNetGet(x, sDownloadArray[x]["URL"], tHeaders, "transferComplete") into tResult -- Increment the number of active requests and flag this item as having been requested if tResult is empty then add 1 to sActiveRequests put "true" into sRequestedArray[x] end if end if -- Stop downloading new files if we have reach the maximum number of simultaneous downloads if sActiveRequests = sMaxRequests then exit repeat end if end repeat end if -- If we have looped through the array and no downloads are occurring, we have finished processing -- all the files to download, so execute the "downloadsCompleted" handler if sActiveRequests = 0 then downloadsCompleted exit downloadURLs end if -- Keep looping if we are not yet finished send "downloadURLs" to me in 50 milliseconds end downloadURLs on transferComplete pID, pResult, pBytes, pCurlCode -- Decrement the number of active requests as one has just finished subtract 1 from sActiveRequests -- Check the status codes and look for success if pCurlCode is 0 and pResult is 200 then -- If successful, retrieve the downloaded data put tsNetRetrData(pID, tError) into tData if tError is empty then -- Downloaded data has been retrieved successfully, save the data to the filesystem -- and update the status of the URL in sDownloadArray put "downloaded" into sDownloadArray[pID]["status"] put tData into URL("file:" & sDataDir & slash & sDownloadArray[pID]["filename"]) -- -- Save the array back to the filesystem so that if the application is closed prior -- -- to all files being downloaded, we can pick up where we left off -- -- removed this next part that creates a json file with the download information -- put ArrayToJSON(sDownloadArray,,true) into URL("file:" & sDataDir & "/URLs.json") end if else -- -- A transfer must have failed, flag the failure and update the URL's status in sDownloadArray put true into sDownloadErrors -- -- removed this next part that creates a json file with the download information -- put "failed" into sDownloadArray[pID]["status"] -- put ArrayToJSON(sDownloadArray,,true) into URL("file:" & sDataDir & "/URLs.json") end if -- Always call tsNetCloseConn to release any memory associated with the transfer tsNetCloseConn pID end transferComplete on downloadsCompleted -- Check for any download errors during processing, and notify the user accordingly if sDownloadErrors is true then put "All downloads attempted, but at least one was unsuccessful. Click the button to retry any failed downloads." after field "Server Response" show fld "Server Response" else put "All downloads completed successfully." into fld "Server Response" OpenLocalFile end if end downloadsCompleted yyyyyyP"[_+Za9Open   cREVGeneral  revUniqueIDyq~wBscripteditorvscrollrscripteditorselection13350  writeFile'p%on mouseUp PublishFile end mouseUp zzP[i+ZaPublish   cREVGeneral  revUniqueIDyq~wBscripteditorvscrollscripteditorselection24 T publishLocalbeon mouseUp if (not the hilite of me) AND (not the hilite of btn "publishRemote") then disable btn "writeFile" set the blendlevel of btn "writeFile" to 50 else -- if the hilite of me then -- set the icon of btn "writeFile" to 24855 --floppy-upload -- if the hilite of btn "publishRemote" then -- set the icon of btn "writeFile" to 24854 -- cloud&floppy-upload -- end if -- else -- set the icon of btn "writeFile" to 1025 --cloud-upload -- end if set the icon of btn "writeFile" to WriteIcon() enable btn "writeFile" set the blendlevel of btn "writeFile" to 0 end if end mouseUp Y>PLocal  cREVGeneral  revUniqueID`L6ZwBscripteditorvscrollscripteditorselection628 U publishRemotebeon mouseUp if (not the hilite of me) AND (not the hilite of btn "publishLocal") then disable btn "writeFile" set the blendlevel of btn "writeFile" to 50 else -- if the hilite of me then -- set the icon of btn "writeFile" to 1025 --cloud-upload -- if the hilite of btn "publishLocal" then -- set the icon of btn "writeFile" to 24854 -- cloud&floppy-upload -- end if -- else -- set the icon of btn "writeFile" to 24855 --floppy-upload -- end if set the icon of btn "writeFile" to WriteIcon() enable btn "writeFile" set the blendlevel of btn "writeFile" to 0 end if end mouseUp l>PWeb  cREVGeneral  revUniqueIDL6ZwBscripteditorvscrollscripteditorselection619 [ openRemotebdSon mouseUp set the icon of btn "readFile" to 1024 -- cloud-download end mouseUp h<VWeb  cREVGeneral  revUniqueIDEpwBscripteditorvscrollscripteditorselection10  \ openLocalbdUon mouseUp set the icon of btn "readFile" to 24889 -- floppy-download end mouseUp WRPLocal  cREVGeneral  revUniqueID EpwBscripteditorvscrollscripteditorselection84  remove-circle.svgP` 8 1~LCD@D@D@????@D-D@HD6B`vD(CDC@D@DD@HD`vD`vD@HDD@D@DCD(C`vD6B@HD@D6BC(C(CC6B@D@DCCC@COCOC@CC@DOCED@ClD@D{DEDlDlDED{D@DlD@CEDOC@DC8DCHDCIDCIDCHDC)D@DHD@/DID1DID3DHD8D8DHD@6DID3DID@/DHD@D)DCHDCIDCIDCHDC8DC@6DC3DC@/DC@DCCCCCCCCCCCCCCCC@DC@/DC1DC3DC8DCP        cREVGeneral  revUniqueIDz~wBtick.svgP` pLCDDC@????fC3JCfCB3*C3jCLUCCDB      cREVGeneral  revUniqueIDK|~wB AyellowLight.png @n&&XPNG  IHDR&&=IDATxԓV+AEwwwpgO~w+w,tzsU0)G#M/xQd k/lC[T(Qi֛#r[LfO\u .:ՠ7@Hޤg/] 7]ս.IZ>@0k#У%BQ+Nڥ>u@|q&&md.4؈+e)5a3םlLHVp\ÎΏSr{/^^ЂyAtx3JFE9\}fj/ neS<>Ϥk<_j(1ɊҀ0mÇ(us0=BJMI\n3rUJw\TݼҔvnmJsfm|8(!>|1-i~:?b %3susp9U$߻HwЂ9d̂)58N?eBGSrduX~3ns{XyUtsub{b\Y ֨b{cڷG8n*/e !Cʳ"SU>%ϣ"ٲⴳ(1ei:޽(f棱+1ɰn\⣴ǰ7;=t/3Uֱ#ձʋ*?+5.+9()ݽ78,蛯Ϲ~Or>a>ծ~=e5ݰ$ٱ~ǚ$2ҡScڰ˻ղԒǷD<„_^2 3Xms~xZ*&pze$$UkX%i[WXۺ+UV* .੖ U韸OݸE%IENDB`  cREVGeneral  revUniqueID'wB BgreenLight.png @n&&CPNG  IHDR&&= IDATxԓEr1 E5L&_DjUU&}K۶ܿI0Qz9(-Gx@V4(AIc{ANlǩ31g=}P5P1 7T5BUXQ)T&Lypsp@PStUjnViU&g`] U:3\x7@'8#Bq(ƣԍg[ZeAϜu !Wk' {@(RS7*s։>}|\o ?qݛ6X#[ɬk۶m۶mޝm۶3k92[ Uu;\Ɖ/wbd#VPܝR$|;~8~߈5ҐA۩ڣxתǍ:!pK[C!HUQXkNuW7>36JÎ"֛ 8.cUyۏ}r#R, Lcʙ &>{}бw,a((-/sSinS-ckKMIa#B˻ah`Cx[+lyʤ2+13fPY6([Ngk-;Ή[U:"!):ޠ_fPOQ,|P+kϕ rmm=-ldD=9T5p'epQRZ"E%sZ3w4| 0 Y_%hnde_FV3 V]ĶiZigiFjf 4M ]LӴ--SH9QzceEQqU*PmӴAiGR8&w2R2:$/湔WIDQvy=w$Aw*vA߽ Q|p(q8%!^)IŹ2se8+=j@IW݅ʻ /w۞/'o%\ 6IENDB`  cREVGeneral  revUniqueID'wB greyLight.png @ n&&KPNG  IHDR&&=jIDATxԖsstp(|'6'oYU.o-=== Y%4[oWRS\\lMXXX'ee>qG\ǖ6=r+l;ɹ ((ȓШ (1@4a{{a't>"[#Y)Cgg )IH)T ge?ԘyK&K|/؛Z[[Z&;;TTT|&:IiHKKKVIHH0J4?OIC[>`@&)SLLo+ Ŵ椘SyQI"h UWJ6-P'4j҃Z'RLê_+|D)**(&^H>@:77LDD=UeŪtebtuu=.gd//޶jL]yGK]1;p%Lྕ:hd_{ftN=p[W;ӷTS9OTeZqjw r֗sߦRF..++; 4 WJ Jqzm_PN03^łjQ/ei,VAlƔPJro.\i; ~`.ȕ ],Cwg n=i^j%xpJFMw_I,--P翎6?$PKSK"ܯD8~%HJ54@NOP.@>%g}bH؁Q: i-eIENDB`  cREVGeneral  revUniqueIDwwB&preferencesGrp ap4on textChanged -- don't let them enter site data until they have created a site if the label of btn "SiteNames" is "Add a site..." then set the uFirstSitePrompt of btn "SiteNames" to "true" -- a flag that changes the dialog text slightly put empty into fld (the short name of the target) send "mouseUp" to btn "addSite" exit textChanged end if enable btn "setPref" -- there has been a change that hasn't been saved set the label of btn "cancelPref" to "Cancel" -- will switch to "Cancel" if "Save" is enabled end textChanged HZF  cREVGeneral  revUniqueIDwBscripteditorvscrollscripteditorselection305'preferencesBackdropK@0pA an  cREVGeneral  revUniqueIDxwB?Round RectangleKA^^^^^^0 Zn profile cREVGeneral  revUniqueIDwB %ftpDirectoryPathPref)h 8pJf/public_html/path/to/calendar_events/directory example: /public_html/yoursite.com/calendar_events_data L  cREVGeneral  revUniqueID`wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' 4setPrefmpon mouseUp StorePreferencesInCustomProps put "true" into pSavePreferences -- to call PreferenceWrite PreferencesSheet pSavePreferences end mouseUp hRSave  cREVGeneral  revUniqueIDpIwBscripteditorvscrollscripteditorselection90 5 cancelPrefep+on mouseUp PreferencesSheet end mouseUp RClose  cREVGeneral  revUniqueIDwBscripteditorvscrollscripteditorselection30 9 ftpServerPref)h 8P4mhost example: ftp://mhost_username.serversignin.com H  cREVGeneral  revUniqueID@}@wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' <ftpPasswordPref)`8-FTP Login PasswordL  cREVGeneral  revUniqueID@|wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' =ftpUserNamePref)`8 FTP Login NameL  cREVGeneral  revUniqueID`|wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' E Label Field sH  cREVGeneral  revUniqueID0\wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Remote URL: ' F URLPathPref)x 3ihttps://site.org/path/to/calendar_events/directory example: https://www.yoursite.com/calendar_events_data .  cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueIDP\wBtablefalsescripteditorselection0scripteditorvscroll  ' G Label Field dJ  cREVGeneral  revUniqueID`\wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Local Path: ' H localPathPref)h 3hLocal/path/to/calendar_events/directory Mac OS example: /Users/yourname/yourwebsite/calendar_events_data (  cREVGeneral  revUniqueIDp\wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' I localPathPrefewon mouseUp answer folder "Save event data..." as sheet put the result into tResult put it into tFolderPath if (tResult is "cancel") OR (it is empty) then exit mouseUp end if put (tFolderPath &SLASH) into fld "localPathPref" end mouseUp LRSet Path  cREVGeneral  revUniqueID\wBscripteditorvscrollscripteditorselection222 J Label Field odN  cREVGeneral  revUniqueIDpwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Server Path: ' K Label Field OdN  cREVGeneral  revUniqueIDpwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse ftp Server: ' L Label Field ,dH  cREVGeneral  revUniqueIDpwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Password: ' M Label Field dH  cREVGeneral  revUniqueIDpwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse User Name: ' N Label Field BBBBBBdJ  cREVGeneral  revUniqueIDpwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse FTP Details ' O Label Field P5-aZK  cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueIDTwBtablefalse  Preferences 'QUseHTMLEntitiesbd'Convert unicode glyphs to html entitiesLUse HTML Entities (recommended)  cREVGeneral  revUniqueIDwB  # SiteNames u local sPreviousMenuNum constant kMain = "Calendar Events" on mouseDown put (the menuHistory of me) into sPreviousMenuNum end mouseDown --|----------------------------------------------------- --| returns the previous label of this button --|----------------------------------------------------- command PreviousLabel return line sPreviousMenuNum of the text of me end PreviousLabel --|----------------------------------------------------- --| Change the name of the current site --| OR --| Switch to a different site --|----------------------------------------------------- on menuPick pChosenItem if pChosenItem is "Add a site..." then dispatch "mouseUp" to btn "addSite" exit menuPick end if -- Change the current site's name if pChosenItem is (line -1 of the text of me) then put line sPreviousMenuNum of the text of me into tPreviousLabel put RemoveIncorrectSiteNames(tPreviousLabel) into tPreviousLabel if trim(tPreviousLabel) is empty then -- there are no current sites set the label of me to "Add a site..." exit menuPick end if ask "Change the site name to:" with tPreviousLabel put the result into tResult put it into tUserChoice replace TAB with empty in tUserChoice replace CR with empty in tUserChoice replace COMMA with "-" in tUserChoice replace SLASH with "|" in tUserChoice if (tResult is "Cancel") OR (tUserChoice is empty) then exit menuPick end if lock screen put the text of me into tSiteListMenu put tUserChoice into line sPreviousMenuNum of tSiteListMenu set the text of me to tSiteListMenu ChangeSiteName tPreviousLabel, tUserChoice set the label of me to tUserChoice unlock screen exit menuPick end if -- switch to the settings of a different site put pChosenItem into tCustomPropertySetName if (the enabled of btn "setPref") OR (the uNewSite of me) then put line sPreviousMenuNum of the text of me into tPreviousLabel answer warning "The"&&tPreviousLabel&&"site hasn't been saved yet." with "Continue" and "Save" lock screen if it is "Save" then set the label of me to tPreviousLabel StorePreferencesInCustomProps --tPreviousLabel PreferenceWrite set the label of me to pChosenItem else if (the uNewSite of me) then put the text of me into tMenuItems filter tMenuItems without tPreviousLabel set the text of me to tMenuItems end if end if set the uNewSite of me to "false" end if put RemoveIncorrectSiteNames(tCustomPropertySetName) into tCustomPropertySetName ClearSiteData if tCustomPropertySetName is not empty then SwitchToSite tCustomPropertySetName -- make this site the current one set the uCurrentSiteName of stack kMain to tCustomPropertySetName -- this is not dependent upon the "Save" btn end if disable btn "setPref" -- Save btn will be disabled until something gets changed set the label of btn "cancelPref" to "Close" -- will switch to "Cancel" if "Save" is enabled unlock screen end menuPick V0 - Edit the site name... uNewSitefalseuFirstSitePromptfalse cREVGeneral  revUniqueID2 wBscripteditorvscrollscripteditorselection2961 #SiteChoiceLabel Us^  cREVGeneral  revUniqueIDPwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse Site Name: ')F deleteSiteGrp iQa^  cREVGeneral  revUniqueIDpz}ރwB)GdeleteSiteBorderKASO&Z profile cREVGeneral  revUniqueIDz}ރwB)H deleteSiteb$  -- simulate a button hilite on mouseDown lock screen -- create the toggle pill hilite set the foregroundColor of grc "siteToggleLine" to 120,120,120 set the dropShadow["angle"] of grc "siteToggleLine" to 90 set the dropShadow["opacity"] of grc "siteToggleLine" to 140 set the dropShadow["size"] of grc "siteToggleLine" to 5 set the dropShadow["distance"] of grc "siteToggleLine" to 2 set the innerShadow["angle"] of grc "deleteSiteBorder" to 90 set the innerShadow["opacity"] of grc "deleteSiteBorder" to 140 set the innerShadow["size"] of grc "deleteSiteBorder" to 2 set the innerShadow["distance"] of grc "deleteSiteBorder" to 1 set the backgroundColor of grc "deleteSiteBorder" to 210,210,210 unlock screen end mouseDown on mouseRelease set the foregroundColor of grc "siteToggleLine" to 170,170,170 set the dropShadow of grc "siteToggleLine" to empty set the dropShadow of grc "deleteSiteBorder" to empty set the backgroundColor of grc "deleteSiteBorder" to 235,235,235 select empty end mouseRelease on mouseUp set the foregroundColor of grc "siteToggleLine" to 170,170,170 set the dropShadow of grc "siteToggleLine" to empty set the dropShadow of grc "deleteSiteBorder" to empty set the backgroundColor of grc "deleteSiteBorder" to 235,235,235 put the label of btn "SiteNames" into tSiteToDelete put trim(RemoveIncorrectSiteNames(tSiteToDelete)) into tSiteToDelete if tSiteToDelete is empty then exit mouseUp end if answer warning "Delete site: "&&tSiteToDelete&&"?"&CR&"This can't be undone." with "Delete" and "Cancel" put it into tUserChoice if tUserChoice is "Cancel" then exit mouseUp end if DeleteSite tSiteToDelete put the text of btn "SiteNames" into tSiteList filter lines of tSiteList without tSiteToDelete put RemoveIncorrectSiteNames(tSiteList) into tSiteList if tSiteList is empty then put "Add a site..." into tSiteList ClearSiteData end if put CR&"-"&CR&"Edit the site name..." after tSiteList set the text of btn "SiteNames" to tSiteList put line 1 of tSiteList into tNewlyActiveSite -- if tNewlyActiveSite is "-" then -- -- there are no more sites -- put "Add a site..." into tNewlyActiveSite -- end if set the label of btn "SiteNames" to tNewlyActiveSite -- activate the newly displayed site set the menuHistory of btn "SiteNames" to 1 -- required? SwitchToSite tNewlyActiveSite -- make this site the current one end mouseUp PTe U-  cREVGeneral  revUniqueIDz}ރwBscripteditorvscrollscripteditorselection1506  )IsiteToggleLineKR_PS`h`  cREVGeneral  revUniqueIDݜރwB)E addSiteGrp iPH\  cREVGeneral  revUniqueIDgރwB)D addSiteBorderKASL&  profile cREVGeneral  revUniqueIDpiރwB$addSiteb$  constant kMain = "Calendar Events" -- simulate a button hilite on mouseDown lock screen -- create the toggle pill hilite set the innerShadow["angle"] of grc "addSiteBorder" to 90 set the innerShadow["opacity"] of grc "addSiteBorder" to 120 set the innerShadow["size"] of grc "addSiteBorder" to 1 set the innerShadow["distance"] of grc "addSiteBorder" to 2 set the backgroundColor of grc "addSiteBorder" to 210,210,210 unlock screen end mouseDown on mouseRelease set the innerShadow of grc "addSiteBorder" to empty set the backgroundColor of grc "addSiteBorder" to 235,235,235 select empty end mouseRelease on mouseUp set the innerShadow of grc "addSiteBorder" to empty set the backgroundColor of grc "addSiteBorder" to 235,235,235 if (the enabled of btn "setPref") OR (the uNewSite of btn "SiteNames") then -- get the name of the current site / button label -- the uCurrentSiteName of stack kMain put the label of btn "SiteNames" into tPreviousLabel put trim(RemoveIncorrectSiteNames(tPreviousLabel)) into tPreviousLabel if tPreviousLabel is not empty then answer warning "The"&&tPreviousLabel&&"site hasn't been saved yet." with "Continue" and "Save" if it is "Save" then PreferenceWrite end if set the uNewSite of btn "SiteNames" to "false" end if end if if the uFirstSitePrompt of btn "SiteNames" then ask "Start by naming your site:" with "My site name" set the uFirstSitePrompt of btn "SiteNames" to "false" else ask "Name your new site:" with "My site name" end if put the result into tResult put trim(it) into tUserChoice replace TAB with empty in tUserChoice replace CR with empty in tUserChoice replace COMMA with "-" in tUserChoice replace SLASH with "|" in tUserChoice put RemoveIncorrectSiteNames(tUserChoice) into tUserChoice if (tResult is "Cancel") OR (tUserChoice is empty) then exit mouseUp end if put the text of btn "SiteNames" into tSiteList if tUserChoice is among the lines of tSiteList then answer warning "There is already a site with that name." exit mouseUp end if -- put line -2 to -1 of tSiteList into tBotttomTest -- put line 1 to -3 of tSiteList into tSiteList put RemoveIncorrectSiteNames(tSiteList) into tSiteList put tSiteList into tSiteListCustomProp put tUserChoice &CR before tSiteList filter tSiteList without empty -- alphabetize the list sort lines of tSiteList ascending put CR&"-"&CR&"Edit the site name..." after tSiteList set the text of btn "SiteNames" to tSiteList set the label of btn "SiteNames" to tUserChoice -- figure out what line the new site is on -- so we can set the menuHistory correctly put 0+0 into tLineCount repeat for each line tSite in tSiteList add 1 to tLineCount if tUserChoice is tSite then lock messages -- so it doesn't trigger the menuPick handler set the menuHistory of btn "SiteNames" to tLineCount unlock messages exit repeat end if end repeat put tUserChoice &CR before tSiteListCustomProp set the uSiteNames of stack "Calendar Events" to tSiteListCustomProp -- clear out the preference fields ClearSiteData -- flag that a new site was created -- so we can warn if they try to switch away without saving set the uNewSite of btn "SiteNames" to "true" enable btn "setPref" -- there has been a change that hasn't been saved end mouseUp PSO \+  cREVGeneral  revUniqueIDiރwBscripteditorvscrollscripteditorselection1373  :CCalendarEventsIcon_PrefbB @HH@-)  cREVGeneral  revUniqueID #wB  gear.svgP`  -LCDBB @????AA`AA AA AA`A AA AA`AAAAAAAA@AqA@A@AqA@AAqAAAAA33AAA33A@AA@AABffAlAB\AffBLABff>AB@ Bl@A̼@A̬@ffA@ffAff@ffAAffA33@lA̜@\A@LA@ff>ڴڴڰ@@̱A33A@A33@AAA@Aff@33Aff@A@A@ffA@AffA@|A@iAff@XAff@ffFA@ff>AA@33@33@@Aff>A@ffFAff@ffVAff@iA@|A@ffA@AffA@A@Aff@33Aff@A@AAA33@A@33Aff>A33AffFAAXAffAiAffA|AAffAAr         cREVGeneral  revUniqueID0wBSpinnerp"on mouseUp hide me end mouseUp X$$icom.livecode.widget.spinner  markerCount @ scaleMarkers  cREVGeneral  revUniqueID dwBscripteditorvscrollscripteditorselection21cloud-check.svgP`:F`LCD#AM A,@????ˡcAҡ@ @ @E2A+@@@@@@S @@@@E A\\A @ @ˡcAҡ@@EA@D@@D@@D@\$Aa@\4AD@         cREVGeneral  revUniqueIDPwB9dataGridWellGrp i` H  cREVGeneral  revUniqueID0]wBscripteditorvscrollscripteditorselection0 fileContentLabel ^^^^^^,ez  cREVGeneral  revUniqueIDwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse File Content '  eventListYear on mouseDoubleUp answer "Create a new document?" with "Yes" OR "No" if it is "Yes" then -- prep for an undo put the dgText of group "EventDataGrid" into tUndoText -- just in case set the uUndoEvent of stack "Calendar Events" to tUndoText set the dgHilitedLines of group "EventDataGrid" to empty -- init set the uEventIndexNumToEdit of stack "Calendar Events" to empty -- init set the label of btn "submit" to "Submit New Event" --set the label of btn "delete" to "Undo" --set the textAlign of btn "delete" to "center" --set the icon of btn "delete" to 1032 --enable btn "delete" --set the blendlevel of btn "delete" to 0 set the dgText of group "EventDataGrid" to empty put empty into fld "eventListYear" set the icon of btn "fileStateIndicator" to 4117 -- gray (no file) disable group "EventDataGrid" end if end mouseDoubleUp P y  cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueIDwBtablefalsescripteditorselection653scripteditorvscroll  ' EventDataGrid(x0 constant kMain = "Calendar Events" --|--------------------------------------------------- --| pButtonNum 1=left 2=middle 3=right --|--------------------------------------------------- on mouseDown pButtonNum dgMouseDown pButtonNum if pButtonNum is 3 then dispatch "mouseUp" to me -- hilite the line if there is a btn "RightClickDGPopUp" then put the dghilitedindex of me into tIndex popup btn "RightClickDGPopUp" --at mouseLoc() end if end if end mouseDown -- put the user-selected event into the editing forms on selectionChanged pHilitedIndex, pPreviouslyHilitedIndex lock screen -- tYear & tUS & tMonthNum & tUS & tDayNum & tUS & tEventTitle & tUS & tEventTime & tUS & tEventDetails put the dgDataOfIndex[pHilitedIndex] of group "EventDataGrid" into tEventArray put tEventArray[rawEvent] into tEventToEdit set the uEventIndexNumToEdit of stack kMain to pHilitedIndex -- put numToNativeChar(29) into tGS put numToNativeChar(30) into tUS -- Unit Separator put numToNativeChar(31) into tLS -- Line Separator set the itemDel to tUS put item 1 of tEventToEdit into tYear put item 2 of tEventToEdit into tMonthNum put item 3 of tEventToEdit into tDayNum put item 6 of tEventToEdit into tEventDetails put (char 1 of tMonthNum) into tRepeatType if (tRepeatType is "W") OR (tRepeatType is "M") then -- repeating event set the hilite of btn "repeating" to "true" dispatch "mouseUp" to btn "repeating" set the itemDel to "|" put item 2 of tMonthNum into tStartMonth put item 3 of tMonthNum into tStartDay put item 4 of tMonthNum into tEndMonth put item 5 of tMonthNum into tEndDay put item 6 of tMonthNum into tDays put item 7 of tMonthNum into tRawExceptionList replace "." with COMMA in tDays put tYear & "-" & tStartMonth &"-"& tStartDay into tStartDate put tYear & "-" & tEndMonth &"-"& tEndDay into tEndDate put WidgetDateToEnglishDate(tStartDate) into fld "startDate" put WidgetDateToEnglishDate(tEndDate) into fld "endDate" if (tRepeatType is "W") then SetFrequencyOptionMenu "W" SetDayHilites tDays put DayNumsToNames(tDays) into tDays replace COMMA with (COMMA & SPACE) in tDays put tDays into fld "days" end if if (tRepeatType is "M") then set the itemDel to COMMA put WeekNumsToOrdinals(item 1 of tDays) into tOrdinal SetFrequencyOptionMenu "M" SetDayHilites (item 2 of tDays) SetOrdinalOptionMenu tOrdinal put tOrdinal && DayNumsToNames(item 2 of tDays) into fld "days" end if -- get the exceptions ( the exception item delimiter is ^ ) -- tExceptionMonthNum . tExceptionDayNum ^ replace "^" with CR in tRawExceptionList set the itemDel to "." repeat for each line tExceptionDate in tRawExceptionList -- put item 1 of tExceptionDate into tExceptionYear -- depreicated put item 1 of tExceptionDate into tExceptionMonth put item 2 of tExceptionDate into tExceptionDay put tExceptionMonth &SLASH& tExceptionDay &SLASH& tYear into tShortDate convert tShortDate to abbreviated english date put tShortDate & CR after tExceptionList end repeat delete char -1 of tExceptionList -- remove trailing CR set the text of btn "exceptionMenu" to trim(tExceptionList) set the label of btn "exceptionMenu" to line 1 of tExceptionList set the selectedDate of widget "Calendar" to tStartDate send "mouseUp" to btn "startDate" -- select the "startDate" field set the itemDel to tUS -- put it back wher it was else -- single event set the hilite of btn "repeating" to "false" dispatch "mouseUp" to btn "repeating" put tYear & "-" & tMonthNum & "-" & tDayNum into tEventDate set the selectedDate of widget "Calendar" to tEventDate put WidgetDateToEnglishDate(tEventDate) into fld "eventDate" end if set the HTMLText of fld "eventTitle" to item 4 of tEventToEdit set the HTMLText of the templateField to item 5 of tEventToEdit put the text of the templateField into tEventTime put the itemDel into tOldItemDel set the itemDel to "-" if item 2 of tEventTime is not empty then put item 1 of tEventTime into fld "eventTimeStart" put item 2 of tEventTime into fld "eventTimeFinish" else put tEventTime into fld "eventTimeStart" end if set the itemDel to tOldItemDel -- tidy set the HTMLText of the templateField to tEventDetails put the text of the templateField into tEventDetails replace tLS with CR in tEventDetails -- the only place we allow CRs set the text of fld "eventDetails" to tEventDetails set the label of btn "submit" to "Update Event" --set the label of btn "delete" to " Delete Event" --set the icon of btn "delete" to 1031 -- -- show btn "delete" --enable btn "delete" --set the blendlevel of btn "delete" to 0 set the itemDel to COMMA -- tidy unlock screen DeleteEventMenuItemEnableDisable "enable" end selectionChanged -- do not allow direct editing of the data grid on EditFieldText -- trap this message so that -- double-clicking the datagrid -- doesn't open the cell for editing end EditFieldText @@@@@@ )! revDataGridLibrary( cGlx2TimeStamp checksum‘åŸè≤ÈÄ òϯB~dgCache data sequencingdgProps - auto hiliteedit mode action select controlJgroup id 1012 of card id 1005 of stack "Data Grid Templates 1614569882798"alternate row colorstruefixed row heightheader background color219,219,219 188,188,188show vscrollbarautoscroll selections into viewtrue scroll when vscrollbar is hiddenfalse row templateJgroup id 1006 of card id 1005 of stack "Data Grid Templates 1614569882798"sort by columntitledim on focusOuttruealternate row colorcolumn marginscolumn properties rawEvent max width1000sort is case sensitive min width40sort direction ascendingwidth100 alignmentleft resizable sort typetextvisiblefalse description max width1000sort is case sensitive min width40widthl@sort direction ascendingencodingmac alignmentleft resizable sort typetextlabel Descriptionvisibletitle max width1000sort is case sensitive min width40width a@sort direction descendingencodingmac alignmentleft resizable sort typetextlabelTitlevisiblemonth max width1000sort is case sensitive min width40width@P@sort direction descendingencodingmac alignmentleft resizable sort typetextlabelDatevisible column widths control type Data Grid show headertruedata can be represented as text allow editingtruestyletablecache controlsedit mode action controlJgroup id 1010 of card id 1005 of stack "Data Grid Templates 1614569882798"edit mode reorder controlJgroup id 1014 of card id 1005 of stack "Data Grid Templates 1614569882798" hilite color row colorshow hscrollbarheader marginspersistent datatrue corner color 232,232,232background color scroll when hscrollbar is hiddenfalse enable swipeleft swipe controlJgroup id 1016 of card id 1005 of stack "Data Grid Templates 1614569882798"multiple linesfalseright swipe controlJgroup id 1019 of card id 1005 of stack "Data Grid Templates 1614569882798"allow column resizingtrueanimate actionscolumns title month description rawEventcolumn divider color 217,217,217header divider threed color 227,227,227header background hilite color194,207,221 125,147,148scrollbar widthautoheader divider color 168,168,168scrollbar corner offset0 row height21 cREVGeneral scripteditorvscroll revUniqueIDwBscripteditorselection5041 dgTrackDragDrop`s, revDataGridLibrary cGlx2TimeStamp checksum‘åŸè≤ÈÄ òϯB~ cREVGeneral  revUniqueID wB  dgAlternatingRowsdJ* jTV* cGlx2TimeStamp checksum cREVGeneral  revUniqueID0wB dgEventCatcher`@:!  cGlx2TimeStamp checksum cREVGeneral  revUniqueID@wB  dgBackground{@! M cGlx2TimeStamp checksum‘åŸè≤ÈÄ òϯB~ cREVGeneral  revUniqueIDPwBdgAlternatingRowsMask x! cGlx2TimeStamp checksum cREVGeneral  revUniqueID`wBdgAlternatingRows[@ !O cGlx2TimeStamp checksum cREVGeneral  revUniqueIDpwB dgListMask y!  cGlx2TimeStamp checksum cREVGeneral  revUniqueIDwB dgHighlightsy!  cGlx2TimeStamp checksum cREVGeneral  revUniqueIDwBdgList y!)! revDataGridLibrary cGlx2TimeStamp checksum‘åŸè≤ÈÄ òϯB~ cREVGeneral  revUniqueIDwBxtitle i@!( yrevDataGridLibrary/xmonth i@A( yrevDataGridLibrary)x description i@( yrevDataGridLibrary+xrawEvent a@! ( yrevDataGridLibrary- dgDividersy!  cGlx2TimeStamp checksum cREVGeneral  revUniqueID wBx[1x[3xS5x[!7dgDropIndicatorq1 ( hrevDataGridLibrary  cGlx2TimeStamp checksum‘åŸè≤ÈÄ òϯB~ cREVGeneral  revUniqueIDpwBdgOval[D Mh cGlx2TimeStamp checksum cREVGeneral  revUniqueIDwBdgLine[* cGlx2TimeStamp checksum cREVGeneral  revUniqueIDwBdgHeaderComponentsy! cGlx2TimeStamp checksum cREVGeneral  revUniqueIDwB dgHeaderMask x!( revDataGridLibrary cGlx2TimeStamp checksum‘åŸè≤ÈÄ òϯB~ cREVGeneral  revUniqueIDwB dgBackground[@@@@@@@!16I cGlx2TimeStamp checksum cREVGeneral  revUniqueIDwBdgHeadery!( *revDataGridLibrary  cGlx2TimeStamp checksum cREVGeneral  revUniqueIDwBxtitle i@!( revDataGridLibraryx BackgroundK@!1!!E}A x HeaderLabel @@!{&  Title ' x LeftHiliteK@!Cx RightHiliteK@Cx SortArrowb@ ] xmonth i@A( revDataGridLibrary x BackgroundC@A1}G x HeaderLabel @@A   Date 'x LeftHiliteK@ax RightHiliteK@ax SortArrow`@ " x description i@( revDataGridLibrary+x BackgroundC@1}e x HeaderLabel @@  Description 'x LeftHiliteK@gx RightHiliteK@gx SortArrow`@   xrawEvent a@d( revDataGridLibrary)x BackgroundC@d1b}c x HeaderLabel @@d  rawEvent 'x LeftHiliteK@ex RightHiliteK@5ex SortArrow`@( & dgHeaderBottomBorder[@@@@@@!  cGlx2TimeStamp checksum cREVGeneral  revUniqueID`wBdgHorizontalComponentsq!i   cGlx2TimeStamp checksum cREVGeneral  revUniqueIDpwB dgHScrollbarYa!i( zrevDataGridLibrarym433 cGlx2TimeStamp checksum‘åŸè≤ÈÄ òϯB~ cREVGeneral  revUniqueIDwB dgCornerPiece[@@@@@@@iO cGlx2TimeStamp checksum cREVGeneral  revUniqueIDP]wB dgScrollbarQ`)! frevDataGridLibraryo<>420 cGlx2TimeStamp checksum‘åŸè≤ÈÄ òϯB~ cREVGeneral  revUniqueIDwBscripteditorvscroll0scripteditorselection0 @fileStateIndicatorB @(&  cREVGeneral  revUniqueIDwB  openPreferencesbcon mouseDown show grc "preferencesBtnBackdrop" set the dropShadow of me to empty end mouseDown on mouseUP lock screen hide grc "preferencesBtnBackdrop" set the dropShadow["size"] of me to 1 set the dropShadow["distance"] of me to 1 set the dropShadow["angle"] of me to 90 set the dropShadow["opacity"] of me to 125 unlock screen PreferencesSheet end mouseUP on mouseRelease lock screen hide grc "preferencesBtnBackdrop" set the dropShadow["size"] of me to 1 set the dropShadow["distance"] of me to 1 set the dropShadow["angle"] of me to 90 set the dropShadow["opacity"] of me to 125 unlock screen end mouseRelease X8' Preferences}ZO Preferences  cREVGeneral  revUniqueIDwBscripteditorvscrollscripteditorselection390 8newFileepconstant kMain = "Calendar Events" on mouseUp if the uFileState of stack kMain is "dirty" then answer warning "The current file has unsaved changes." with "Cancel" and "Create New" if it is "Cancel" then exit mouseUp end if end if put the last word of the long date into tCurrentYear ask "What year will these events be for?" with tCurrentYear put trim(it) into tYear if tYear is empty then exit mouseUp end if if (tYear is not a date) OR ((the num of chars of tYear is not 2) AND (the num of chars of tYear is not 4)) then answer QUOTE&tYear"E && "isn't a reasonable year." with "Use Anyway" and "Oh" if it is "Oh" then exit mouseUp end if end if -- in case they entered a 2 digit year if the num of chars of tYear is 2 then if tYear > 60 then -- will work until 2060 :^) put "19"&tYear into tYear else -- assume we aren't making a calendar for the 1800s put "20"&tYear into tYear end if end if if tYear > tCurrentYear + 5 then answer tYear&"... Wow, you must be a psychic!" with "Cancel" and "I Am" titled "Rasputin" --as sheet if it is "Cancel" then exit mouseUp end if end if NewDocument tYear end mouseUp yyyyyyPOZ+ZNew File  cREVGeneral  revUniqueIDwBscripteditorvscrollscripteditorselection1274  QServer Responsex5on mouseUp hide fld "Server Response" end mouseUp P 6  cREVGeneral  revUniqueID,-wBtablefalsescripteditorselection40scripteditorvscroll cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  Uploading file 2024_calendar_events.txt to ftp://ua873228.serversignin.com/public_html/traditionaltaekwondo.org/calendar_events_data ' /Transfer complete with server response code 226 '^ ' tRecvHeaders ' >220---------- Welcome to Pure-FTPd [privsep] [TLS] ---------- '| *220-You are user number 5 of 188 allowed. 'T .220-Local time is now 08:31. Server port: 21. '\ 2220-This is a private system - No anonymous login 'd 6220-IPv6 connections are also welcome on this server. 'l =220 You will be disconnected after 30 minutes of inactivity. 'z ,500 This security scheme is not implemented 'X 234 AUTH TLS OK. '" (331 User ua873228 OK. Password required 'P *230 OK. Current restricted directory is / 'T 200 PBSZ=0 ' +200 Data protection level set to "private" 'V !257 "/" is your current location 'B *250 OK. Current directory is /public_html 'T C250 OK. Current directory is /public_html/traditionaltaekwondo.org ' X250 OK. Current directory is /public_html/traditionaltaekwondo.org/calendar_events_data ' 500 Unknown command '( 3227 Entering Passive Mode (192,99,207,157,134,205) 'f 200 TYPE is now 8-bit binary ': 150 Accepted data connection ': "226-File successfully transferred 'D :226 0.000 seconds (measured here), 5.17 Mbytes per second 't +tsNetGetTimeouts: 60,0,300000,60000,30,1000 'VV `6tr a i \progress@"GIF89adbd424<:<,*,DBDܔ  $&$LNL|z|̄dfd|~|Č $"$ljl! NETSCAPE2.0! XMP DataXMP ʰɱܳٲDzԳᾱ󲵴ڱ峦հڴܳ۳ݰ³ձճϱʰѳҹ󷡶ٰ?>=<;:9876543210/.-,+*)('&%$#"!  !,pH,Ȥrl:ШtJZجvzxL.@~sQ_>Hp|srFtvxGzq}s,.--.,D.',CC,BBF%E'.1F0F1-.E'D՛CՓB//F/E;jB@I k8?~ b=z<Pd$HI+&tD-o2 DZ6~礹v|0았Nх>@UMirϊ9 2h7U BFj" ̯i/y-(bBP&v{o@mcj&ĀR !+lϠCMӨS^ZK!,t  Ȥrl:hsRvz&`, zfJd2!dZ+I.'{N-dxH'x'cG-JR}K\wmosKv]0i^.aqfNO+QFU)Ӡ zA!,u 0d r9|ШtJZXe$ @($PYFٴzͦ6^o|~ϏqqJ#P}T]B66Q'(/J--R-'--PSPQzJ0WL0z'B*OBL.X,Q.l,vaKp^xM!DkK XPcA%p$Sd )!,@04*pl:ШtJZa2a O#q$un`N4]8!)*)oP {{B1vL&t n"tB**jOg1$$C22+/,C--m!Pʸ&BգQSo!'/R޺4~B+{ gM\xF '.EpBy :AHEDx+ Q*qE,x H(0>Y CܔLȺ0Q~CLȡCě veM0diI=2v€M40+' HI+^$!,ipH, J0:ШtJ>%dӱ?z."F$N\J)Ypa&fphp(iҸx!",d@pH,aPiɨtJZc2! $D#i$uͶ:<(V9nY*)mCr `BtEyq&GPHB" gE,"- gC+F''-˷ "(C-E-'΅ѺC'F!l"r!7PH0 !ɁeEbV  P5RA"JDLP`BV)c$u33E{@ [$Z&``%Ba-SNqĉI 0m ! ,6C@p1Tpl:bL&hp2D󑚣`f8 {8!)J4:l[B pmog"B*JB#M.'kBB66N'--~$e(/C-!O-j$bȜ˜MBg# C߼-0NC eDzPɉ.KHk@mP;K r (.Q XPBAUfb .8p0!  I !,|pH,ȤrYÇIZ,"L&id83պ #b`*G)*)qV }}B1xE&v E"vB**muj1$$C22+o/,C--X˪&BդI!C4B+H.0LH 5B^ŚuVCHY(90o ::7TDZ&P :,%<5…I &$([4h4 V$NJ9$!,pH,ȤrL !ЬZج*A&FpL48FBlF @zF#*rq ~~dBtyE ~ KwCD#F v T166F0-'U77BMq2B'-F!Cu7rkJ%܅茜hp\ $P$(X|Єb)pc#`CP3b!0(Q+Ln2%ըlUC$`+Id$]S(U! D$&LXѦ$`DH #Xt11?$״mHkH!,@pH,Ȥrl*?JCqZجv4 e8~L|`xX9G*#sp#C Ex&L"w"B 5*o#w$$1C++KBMq,ɗ/C--v p$ JC t4kځ ,aPh"1ÈL*^1MB]Z&B" bZ A` ֳf&e`DBE.$!:\ʴ KbG:IKAFBVZ1H/ 0H384H R*ҠQA!!,pH,Ȥrl:Ca<جv  !9&N#m[C@GaPခ!u eBwXBVEt l !p!n!ZCW C ~BMLcKG_&``@ƌ"IE&LɈp,ec+A!,@pH,Ȥrl:Ï0جv}BɄ2t贚XpcMۑ|/(h||"O#{F p$MhoJn$|LK | KLLgJ# ["wgGMyPLi IJXsH\q:roB&0׽#d$P D)*h0!5h% ,iɄ&'/ļXD+C!,pP*Ȥrl:Шt,%#QzXZjJww\~ B"|qLxS%MLQoNPILtM$bK]OPQnMOK~SݏeIjBQ#SPegC N&^m /8dWLB<$+@l8d0P(c0$F40@¶jNX\2J'7lA!,pH, KȤQX:ШtJrYJ42Gd-tidR2T9 <%&wB&{%F { % F cB5N*CV%+D+d$N$dN%-N-O,dķ%' ε%CF4zܣ%*鑁D CEgmqTPꀬ")4UØ=s>80Ä%L`"ʄ'1|2㔍4R#$[zҠ(5I&>i6!,pH,Hrl:ШtJZH-%N#Q4K* FY%((t L E MzB|%,.--.,\D#NDPC"T%'-'.1E6P6C1PK%//E-P!C2P%U~P7RSQʶ!D'P-O273 pAXYlk`O*FE x)Jr2RJM'B| 5dzJb;Դ $ӠB48'`Q#$ZIT %*P1@!,`!g#Lq$cP8-$g ˌTV%U!LqU%F3w,!,|pH,Hrl:ШtJ [J88J.Q.R((G& L xNC#}U%.'-.,kNqB*P*O%--''E+N2+C 4P$1%# ƕ%//E-P-C,PΫQRUSɛ'!D'C/O SzHH!>>_m LB 2 %¯'$ %3@|@ʃBQ Y'<Pɨ! N=IU[I$!iɣe$T1Pe?) R3 HR;BbjO:EU>.^fphjZR@H c` ! ,hpH,Hd@l:ШtJ&5pRuL.WPY a ~o.}.((ZF uu xDxM| ~,.--.,Cu#CKLg%1'-'.1E6u6dz/,E--C(yS%ʜ'!-/E'~P.ฺB¾h8yuo)SC>X! "PdNJ.^5BELx@RRL AgwVPF NE,jr('H)4,`c"OĔOI@pi0c1ez+"^!,-_pH,Ȥr8ШtJZLL88J.$P z~-}*((G& P wRC#|z%.'-.,DRpZ*T*g%0-''E+R2+C 4T$1%V%/-'!-/E-T-C,TddD'C/SjW$ Q!.ʪ%ȳD2Rr k GIqAJ"PU"A)]r$ 4U Y(L-"sNI" \8@NHI $8 /TFʰ,!,pH,Ȥrl:ШtJN?e4ְxlY* X pP~{%((YF v sDxt,.--.,C x#Cz'%1''1E6z6z,w-y%//Ey-C{(~}-p,F/'D}{|yо1˓[0 2/27/21 set the itemDel to SLASH put "20" & item -1 of tRawDate into tYear put item 2 of tRawDate into tDay put item 1 of tRawDate into tMonth put (tYear &"-"& tMonth &"-"& tDay) into tWidgetDate ask information "Enter a date" with tWidgetDate titled "Custom Date" put it into tDate if (tDate is "Cancel") OR (tDate is empty) then exit mouseDoubleUp end if put empty into tFormatErr -- init set the itemDel to "-" -- parse the user's date format for glaring errors if the number of items of tDate is not 3 then put "error,You must indicate: Year-Month-Day." into tFormatErr end if if (item 1 of tDate is not a number) OR (item 2 of tDate is not a number) OR (item 3 of tDate is not a number) then put "error,The date items must all be numbers." into tFormatErr end if if (the num of chars of item 1 of tDate is not 2) AND (the num of chars of item 1 of tDate is not 4) then put "error,The year is not formatted correctly." into tFormatErr end if put item 1 of tDate into tUserYear if the num of chars of tUserYear is 2 then if tUserYear < 60 then put 20&tUserYear into tUserYear end if end if if (tUserYear > tYear + 6) then answer tUserYear&"... Wow! You are psychic." with "I Am" titled "Rasputin" end if set the itemDel to COMMA -- tidy if item 1 of tFormatErr is "error" then answer item 2 of tFormatErr exit mouseDoubleUp end if put tDate into fld "eventDate" end mouseDoubleUp Pick a date on the calendar. %  cREVGeneral  revUniqueIDpĻswBtablefalsescripteditorselection50scripteditorvscroll cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  ' $siteNameP$+Z|  cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueIDЧwBtablefalse ''X delete.svgP`a1LCDzDzD5@????CNC ACCFDwDNCC@kD@:DLC33BCLCC@kD@:DffCf,DͬDͬDf(the height of fld tChosenItem) then set the fixedLineHeight of fld tChosenItem to "true" repeat 3 times if (the formattedHeight of fld tChosenItem)>(the height of fld tChosenItem) then set the textSize of fld tChosenItem to (the textSize of fld tChosenItem - 1) else exit repeat end if end repeat end if unlock screen end menuPick X+General Repeating Events File Structure FTP  cREVGeneral  revUniqueID`_~wBscripteditorvscrollIscripteditorselection740 *Y generalInfoP=f T  cREVGeneral  revUniqueID@_calendar_events.txt for example: 2021_calendar_events.txt ' ' CEvent Time may be left empty or entered as free-form text... but if it is structured using the time-picker then it will be placed into the calendar chronologically relative to any other events that day. If the calendar is unable to determine what time the event starts, the event will be placed at the beginning of the day. ' ' The Event Description text can be styled using html tags (see the "Text" menu for ideas). Keep in mind that the description will be wrapped inside of

tags when it is placed into the Side-Bar. If you want to place something only in the Side-Bar without having the title show up inside of any day square, choose "For Side-Bar Only" from the "Edit" menu. If you want this event to show up at the top or bottom of the Side-Bar, put the word "top" or "bottom" in an event time field. ' *[repeatingEventsInfoP=` V  cREVGeneral  revUniqueIDP\wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  Repeating events are currently limited to ones that occur every week or once per month. For weekly events this could be things like: "every Saturday" or "every Tuesday and Thursday". For monthly events this might be: "every third Friday" or "the last Monday" of each month. For events that happen more than once a month but not every week, the event will need to be represented by multiple entries. ' ' BRepeating events don't need an end date if they continue all year. ' ' To change an individual day in a repeating event, use the "+" to add an exception date. This will essentially cancel that one date in the larger repeating event. Then you can add a single event if you need to change the time or day of the one event day that you cancelled. ' *\fileStructureInfoP>r P  cREVGeneral  revUniqueID owBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  &For developers using the LiveCode IDE: #L The text file itself is formatted as UTF-8. The structure for holding each event is a series of items on a line: Year • MonthNum • DayNum • EventTitle • EventTime • EventDetails 'j ' lThe itemDelimiter (represented by "•" above) is the untypable character: numToNativeChar(30) [ASCII 30] ' ZEach event is separated by (lineDelimiter) the untypable numToNativeChar(31) [ASCII 31] ' ' WIf the event is a repeating event then instead of a MonthNum, the second item contains: ' W [or M] | StartMonthNum | ExactStartDayNum | EndMonthNum | ExactEndDayNum | DaysOfTheWeekNums or OncePerMonth | SpecialCaseCancelDates ' ' Repeating event items are delimited by the "pipe" character. The DaysOfTheWeekNums are the numbers 1 through 7 with 1 = Sunday, 2 = Monday, and so on. The numbers are separated with periods. So if an event repeated every Monday, Wednesday and Friday this would be represented by 2.4.6 Monthly repeating event are indicated with an"M" OncePerMonth is the number 1-6 representing the week (with 6 representing the LAST week of the month regardless of it's actual number.) a period delimiter and then a number 1-7 for the day of the week. So, "every 3rd Thursday" would be represented by 3.5 The SpecialCaseCancelDates are formatted as Year.MonthNum.ExactDayNum with a period as the itemDelimiter. The caret "^" character is used as the delimter between each SpecialCaseCancelDate. ' :BButtonbB oon mouseUp -- launch url "/livecode/calendarevents.html" GoURL end mouseUp @HH@-)  cREVGeneral  revUniqueID0wBscripteditorvscrollscripteditorselection110  DftpInfoP=f ~  cREVGeneral  revUniqueID3+wBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  If you wish to FTP the file directly onto your server, the FTP information can be stored (in a lightly encrypted preference file) by adding it to the Preference sheet. 'N ' &For developers using the LiveCode IDE: #L BThis project uses TSNet to handle FTP protocols and was built using a LiveCode plan where the editionType property is "professional". If you are using the source file of this project with a LiveCode plan where getting the editionType returns something other than "professional" (such as "community" or "commercial"), then the FTP script for publishing the calendar event file will likely fail without additional code to make use of LibURL. I have attempted to add the code for LibURL but apparently I have munged it in some way. Perhaps you will see how to fix it ¯\_(ツ)_/¯ '| pElementary91ֱLinkon linkClicked pLink -- the full URL is too long to display easily -- so give the user a general idea -- but leave the specific path to the script -- launch url "/livecode/calendarevents.html" GoURL end linkClicked P!    cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueID_xBtablefalsescripteditorselection260scripteditorvscroll   #< s versionLabel @K-  cREVGeneral  revUniqueID`!TxBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  version: ' s versionNumber@B-  cREVGeneral  revUniqueIDp!TxBtablefalsescripteditorselection0scripteditorvscroll cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  1.2.8 ' -)gRevAppIcon.png @@@v hPNG  IHDR@@iq /IDATx[ضm۶mvnl۶m5׶vWug$o򲪾3T4T \b @S@ -Ăd@k0J^ `u4A(7G) *Hk X E9@E RK& sJ ڂY`xfT0.ip(1^^op(d y4&h@D0`"P*ƠH 4$㕀Ru0`&E%ZԴ, (uTނ)j1h#XM(  ѿF>h zw ` @b&UT1I&37%K2eJӫe\ ~%KZWVͲiӦ߻vj9l03gڭ\iΝN}vׯ---ÃX+W1I$gϞݵ`KQuͭwn=b[L~ժU.{ǰ+<(((1TR%O 7n IQGrx.C,+:I} K.2}3fΞ=c|+fV|y:uiF7qOժU3J*L2y-[Vr 4еo~㟋/۶mɓ'n߾m[Nceit!פ\"5k9)66>>>C`` C߾}Çyf oǎ:u*ҁdL")6Calendar Events)Sa  cREVGeneral  revUniqueIDRwBscripteditorvscrollscripteditorselection0   6repeatingDayTypeLabel PA H  cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueIDRwBtablefalse  every ' 6repeatingWeekOrMonthƥ }on menuPick pChosenItem if pChosenItem is "monthly" then show btn "repeatingDayType" -- only allow ONE day to be selected -- if multiple days then clear them all put 0 into tHiliteCounter repeat with x = 1 to the number of btns in grp "DayNamesGrp" if the hilite of btn x of grp "DayNamesGrp" then add 1 to tHiliteCounter end if end repeat -- see if the "day" field text is the right kind put the text of btn "repeatingDayType" into tDayTypeList put word 1 of fld "days" into tDayTypeWord if tDayTypeWord is not among the lines of tDayTypeList then put 1+1 into tHiliteCounter -- hijack this flag to clear everything end if if (tHiliteCounter > 1) OR ((the num of items of fld "days") > 1) then -- remove all the hilites from days of the week repeat with x = 1 to the number of btns in grp "DayNamesGrp" set the hilite of btn x of grp "DayNamesGrp" to "false" end repeat put empty into fld "days" end if else -- Weekly hide btn "repeatingDayType" put the text of btn "repeatingDayType" into tDayTypeList -- remove possible "Month" syntax from the repeating days field put fld "days" into tDayText repeat for each line tDayType in tDayTypeList if word one of tDayText is tDayType then repeat with x = 1 to the number of btns in grp "DayNamesGrp" set the hilite of btn x of grp "DayNamesGrp" to "false" end repeat put empty into fld "days" exit repeat end if end repeat end if focus on nothing end menuPick G MonthlyWeekly Monthly  cREVGeneral  revUniqueIDRwBscripteditorvscrollscripteditorselection470 7repeatingDayTypeƥ }on menuPick pChosenItem repeat with x = 1 to the number of btns in grp "DayNamesGrp" if (the hilite of btn x of grp "DayNamesGrp") then --put the label of the target into tDay put the short name of btn x of grp "DayNamesGrp" into tDay -- ex: "repeatingThursday" replace "repeating" with empty in tDay -- ex: "Thursday" delete char 4 to -1 of tDay -- ex: "Thu" put pChosenItem && tDay into fld "days" end if end repeat focus on nothing end menuPick  A first$first second third fourth fifth last  cREVGeneral  revUniqueIDPWҗwBscripteditorvscrollscripteditorselection404 'Y numberPadGrpaPon mouseUp put the short name of the target into pKey if the selectedField is empty then select after fld "eventTimeStart" end if put the short name of (word -2 to -1 of the selectedChunk) into tSelectedFld if tSelectedFld is not among the items "eventTimeStart,eventTimeFinish" then select after fld "eventTimeStart" put "eventTimeStart" into tSelectedFld end if switch pKey case "am" put " A.M." after the selectedChunk break case "pm" put " P.M." after the selectedChunk break case "colon" put ":" after the selectedChunk break case "back" -- delete A.M. or P.M. as a whole chunk -- otherwise just delete back one char at a time put (word 4 of the selectedChunk) into tDeleteCharNum if (char (tDeleteCharNum - 4) to tDeleteCharNum of fld tSelectedFld) is among the items " A.M., P.M." then delete char (tDeleteCharNum - 4) to tDeleteCharNum of fld tSelectedFld else -- regular delete back 1 char delete char (word 4 of the selectedChunk) of fld tSelectedFld end if break default put the label of the target after the selectedChunk break end switch end mouseUp   cREVGeneral  revUniqueIDHwBscripteditorvscrollscripteditorselection0'Boneeo'& 1  cREVGeneral  revUniqueID yJwBscripteditorvscrollscripteditorselection0 'Ctwoeo'& 2  cREVGeneral  revUniqueID0yJwB 'Dthreeeo'& 3  cREVGeneral  revUniqueID@yJwB 'Kameo.'& A.M.  cREVGeneral  revUniqueIDPyJwB 'Lfoureo'& 4  cREVGeneral  revUniqueID`yJwB 'Mfiveeo'& 5  cREVGeneral  revUniqueIDpyJwB 'Nsixeo'& 6  cREVGeneral  revUniqueIDyJwB 'Opmeo.'& P.M.  cREVGeneral  revUniqueIDyJwB 'Pseveneo'& 7  cREVGeneral  revUniqueIDyJwB 'Qeighteo'& 8  cREVGeneral  revUniqueIDyJwB 'Rnineeo'& 9  cREVGeneral  revUniqueIDyJwB 'Tzeroeo5s& 0  cREVGeneral  revUniqueIDyJwB 'Vcoloneo.'& :  cREVGeneral  revUniqueIDyJwB 'WbackCg.5'&'X'X  cREVGeneral  revUniqueID`m9wB :e Menubar 1@Ie  cREVGeneral  revUniqueID_wBscripteditorvscrollscripteditorselection0:fFileU on menuPick pWhich switch pWhich case "New File" dispatch "mouseUp" to btn "newFile" of stack "Calendar Events" break case "New Event" dispatch "mouseUp" to btn "clear" of stack "Calendar Events" break case "Open" dispatch "mouseUp" to btn "readFile" of stack "Calendar Events" break case "Publish" case "Save" PublishFile break case "Save As" case "Save As…" case "Duplicate As…" case "Rename File" put "true" into tSaveAs PublishFile tSaveAs break case "Quit Calendar Events" case "Quit" case "Exit" Quit break end switch end menuPick !FileFNew &File/N New &Event/E &Open/O &Publish/S &Duplicate As… - &Quit/Q  cREVGeneral  revUniqueID_wBscripteditorvscroll$scripteditorselection613 :gEditU S local sUndoFieldName -- to allow undoing of text formatting by menu local sUndoText -- to allow undoing of text formatting by menu on menuPick pWhich switch pWhich case "Undo" undo break case "Undo Text Formatting" put the text of fld sUndoFieldName into tNewUndoText set the text of fld sUndoFieldName to sUndoText -- put empty into sLongUndoFieldName -- put empty into sUndoText -- UndoMenuNameChange -- will default to "Undo" -- disable menuItem 1 of btn "Edit" UndoTextFormatting tNewUndoText break case "Undo Clear Event" UndoNewEvent break case "Undo New File" case "Undo Delete Event" -- in both cases the dgText is restored UndoDeletedEvent break case "Redo Event" RedoNewEvent break case "Delete Event" put empty into tHilitedIndex -- init put the dgHilitedIndexes of group "EventDataGrid" into tHilitedIndex if tHilitedIndex is not empty then -- an event is selected in the dataGrid DeleteEvent end if break case "Cut" cut break case "Copy" copy break case "Paste" paste break case "Clear" clear break case "Select All" if the selectedChunk is not empty then select the text of fld (word -1 of the selectedChunk) end if break case "For Side-Bar Only" put "*SideBarOnly*" into fld "eventTitle" break case "Preferences" PreferencesSheet break end switch end menuPick --|------------------------------------------------------ --| --|------------------------------------------------------ command UndoTextFormattingTarget pFieldName if there is a fld pFieldName then put pFieldName into sUndoFieldName end if end UndoTextFormattingTarget --|------------------------------------------------------ --| --|------------------------------------------------------ command UndoTextFormatting pUndoText if pUndoText is empty then -- undo action else -- store the original text in case we want to undo later put pUndoText into sUndoText end if end UndoTextFormatting "#Editz(&Undo Clear Event/Z (Delete Event (-/^@z Cu&t/X &Copy/C &Paste/V Clea&r Select &All/A - For Side-Bar Only - Preferences/,   cREVGeneral  revUniqueID_wBscripteditorvscrollscripteditorselection1912 :hTextU O on menuPick pWhich if the selectedChunk is empty then exit menuPick -- no field has focus end if put the selectedText into tSelectedText -- "Link" menuItem scripting is getting confused about the selectedChunk put word 2 of the selectedChunk into tSelectedStartChar put word 4 of the selectedChunk into tSelectedEndChar put (word -1 of the selectedChunk) into tFieldName put the text of fld tFieldName into tUndoText if "eventPaneGrp" is not in the long name of fld tFieldName then exit menuPick -- not a field that should be targeted end if put the short name of fld tFieldName into tShortTargetFldName switch pWhich case "Bold" if tSelectedText is not empty then put (""&tSelectedText&"") into the selectedChunk end if break case "Italic" if tSelectedText is not empty then put (""&tSelectedText&"") into the selectedChunk end if break case "Underline" if tSelectedText is not empty then put (""&tSelectedText&"") into the selectedChunk end if break case "Span" if (tShortTargetFldName is "eventDetails") AND (tSelectedText is not empty) then put (""&tSelectedText&"") into the selectedChunk end if break case "Link" if (tShortTargetFldName is "eventDetails") then if ("http://" is in tSelectedText) OR ("https://" is in tSelectedText) then -- they selected the URL -- ask what visible text should be "linked" with the URL put ("" && "Put_the_visible_text_here" & "") into tHTMLString ask "What text should contain the link?" if (the result is not "cancel") AND (it is not empty) then replace "Put_the_visible_text_here" with it in tHTMLString end if else -- they selected non-URL text -- ask what URL should be "linked" to this text put ("" && tSelectedText & "") into tHTMLString ask "What URL should be used for the link?" if (the result is not "cancel") AND (it is not empty) then replace "Put_the_URL_here" with it in tHTMLString end if end if -- we were having problems with the selectedChunk getting munged put tHTMLString into char tSelectedStartChar to tSelectedEndChar of fld tShortTargetFldName end if break end switch -- a minor bug: -- putting a link framework into an empty fld -- will not be "undo-able" because tUndoText will be empty if tUndoText is not empty then UndoMenuNameChange "Text Formatting" send "UndoTextFormattingTarget" && tFieldName to btn "Edit" send "UndoTextFormatting" && tUndoText to btn "Edit" end if end menuPick E'Text0&Bold/b &Italic/i &Underline/u - Spa&n/h &Link/l  cREVGeneral  revUniqueID_wBscripteditorvscrollscripteditorselection2735 CHelpU on menuPick pWhich switch pWhich case "Help" InformationSheet break case "91ֱ" if "about" is among the lines of the openStacks then close stack "about" else put the loc of stack "Calendar Events" into t91ֱLoc subtract 100 from item 2 of t91ֱLoc -- raise the 91ֱ window slightly set the loc of stack "about" to t91ֱLoc go stack "about" end if break end switch end menuPick l'Help&Help - &91ֱ  cREVGeneral  revUniqueID`ԏwBscripteditorvscrollscripteditorselection3 ?{ strip-n-shipep--|------------------------------------------------------ --| Clean it up for creating a standalone --|------------------------------------------------------ on mouseUp StripAndShip end mouseUp command StripAndShip ClearItOut "true" -- just being careful ProgressBar "stop" RetryProgress "stop" NewDocument -- this is an unused custom prop (feature) in btn "readFile" set the uEventList of stack "Calendar Events" to empty -- temporarily remove any preferences -- by renaming the file -- use btn "recoverPreferences" to change the name back put PreferencePath() into tPrefPath if there is a file tPrefPath then set the itemDel to SLASH put tPrefPath into tNewPrefPath put "~" before item -1 of tNewPrefPath set the itemDel to COMMA if there is a file tNewPrefPath then -- overwriting this file will probably indicate -- that we are replacing the good original file -- with a new empty preference file -- this can happen if we click "Strip-N-Ship" -- more than once answer "It looks like there is already a stored preference file. Are you sure you want to overwrite it?" with "Overwrite" OR "Cancel" if it is "Overwrite" then rename file tPrefPath to tNewPrefPath end if else rename file tPrefPath to tNewPrefPath end if end if -- delete the preferences stored in custom props put the uSiteNames of stack "Calendar Events" into tSiteNames repeat for each line tSiteName in tSiteNames -- SwitchToSite tSiteName DeleteSite tSiteName end repeat -- clear the Preferences pane ClearSiteData set the label of btn "SiteNames" to empty set the text of btn "SiteNames" to empty put empty into fld "siteName" -- set some initial button states set the hilite of btn "openLocal" to "true" set the hilite of btn "openRemote" to "false" set the hilite of btn "publishLocal" to "true" set the hilite of btn "publishRemote" to "false" if the hilite of btn "repeating" then set the hilite of btn "repeating" to "false" send mouseUp to btn "repeating" end if hide fld "devNotes" end StripAndShip *k%Clean up before creating a standalone Strip-N-Ship  cREVGeneral  revUniqueID0gHwBscripteditorvscrollscripteditorselection99 ?|recoverPreferencesep--|------------------------------------------------------ --| Return the preferences file to its original name --| so that this app can see it again --|------------------------------------------------------ on mouseUp put PreferencePath() into tPrefPath set the itemDel to SLASH put tPrefPath into tNewPrefPath put "~" before item -1 of tNewPrefPath set the itemDel to COMMA if there is a file tNewPrefPath then rename file tNewPrefPath to tPrefPath end if PreferenceRead end mouseUp i*Recover Preferences  cREVGeneral  revUniqueIDWIwBscripteditorvscrollscripteditorselection513 H|devNotesep"--|------------------------------------------------------ --| Show and hide some developer notes --| --|------------------------------------------------------ on mouseUp if the vis of fld "devNotes" then hide fld "devNotes" else show fld "devNotes" end if end mouseUp *[ Dev Notes  cREVGeneral  revUniqueIDǐwBscripteditorvscrollscripteditorselection290 H}devNotes!`VuT cellFormattingfalse cREVGeneral  revUniqueID@ǐwBtablefalse cRevTable  cellFormatfalsemaxColumnCountcellEditfalse  Strip-N-Ship will rename the preference file so that FTP login information won't be saved into custom props during the standalone buider process. Recover Preferences just returns things to the way they were. # ' #$& 'JT ' &Calendar Events uses these Inclusions: 'L  DataGrid '$  Internet '$ / Revolution Zip (an external .bundle) '^ ) mergJSON (an external .bundle) 'R & tsNet (an external .bundle) 'L ' 1------------------------------------------------- 'b ' 1------------------------------------------------- 'bH~ `VWRMC retryProgress@"GIF89adbd424<:<,*,DBDܔ  $&$LNL|z|̄dfd|~|Č $"$ljl! NETSCAPE2.0! XMP DataXMP ʰɱܳٲDzԳᾱ󲵴ڱ峦հڴܳ۳ݰ³ձճϱʰѳҹ󷡶ٰ?>=<;:9876543210/.-,+*)('&%$#"!  !,pH,Ȥrl:ШtJZجvzxL.@~sQ_>Hp|srFtvxGzq}s,.--.,D.',CC,BBF%E'.1F0F1-.E'D՛CՓB//F/E;jB@I k8?~ b=z<Pd$HI+&tD-o2 DZ6~礹v|0았Nх>@UMirϊ9 2h7U BFj" ̯i/y-(bBP&v{o@mcj&ĀR !+lϠCMӨS^ZK!,t  Ȥrl:hsRvz&`, zfJd2!dZ+I.'{N-dxH'x'cG-JR}K\wmosKv]0i^.aqfNO+QFU)Ӡ zA!,u 0d r9|ШtJZXe$ @($PYFٴzͦ6^o|~ϏqqJ#P}T]B66Q'(/J--R-'--PSPQzJ0WL0z'B*OBL.X,Q.l,vaKp^xM!DkK XPcA%p$Sd )!,@04*pl:ШtJZa2a O#q$un`N4]8!)*)oP {{B1vL&t n"tB**jOg1$$C22+/,C--m!Pʸ&BգQSo!'/R޺4~B+{ gM\xF '.EpBy :AHEDx+ Q*qE,x H(0>Y CܔLȺ0Q~CLȡCě veM0diI=2v€M40+' HI+^$!,ipH, J0:ШtJ>%dӱ?z."F$N\J)Ypa&fphp(iҸx!",d@pH,aPiɨtJZc2! $D#i$uͶ:<(V9nY*)mCr `BtEyq&GPHB" gE,"- gC+F''-˷ "(C-E-'΅ѺC'F!l"r!7PH0 !ɁeEbV  P5RA"JDLP`BV)c$u33E{@ [$Z&``%Ba-SNqĉI 0m ! ,6C@p1Tpl:bL&hp2D󑚣`f8 {8!)J4:l[B pmog"B*JB#M.'kBB66N'--~$e(/C-!O-j$bȜ˜MBg# C߼-0NC eDzPɉ.KHk@mP;K r (.Q XPBAUfb .8p0!  I !,|pH,ȤrYÇIZ,"L&id83պ #b`*G)*)qV }}B1xE&v E"vB**muj1$$C22+o/,C--X˪&BդI!C4B+H.0LH 5B^ŚuVCHY(90o ::7TDZ&P :,%<5…I &$([4h4 V$NJ9$!,pH,ȤrL !ЬZج*A&FpL48FBlF @zF#*rq ~~dBtyE ~ KwCD#F v T166F0-'U77BMq2B'-F!Cu7rkJ%܅茜hp\ $P$(X|Єb)pc#`CP3b!0(Q+Ln2%ըlUC$`+Id$]S(U! D$&LXѦ$`DH #Xt11?$״mHkH!,@pH,Ȥrl*?JCqZجv4 e8~L|`xX9G*#sp#C Ex&L"w"B 5*o#w$$1C++KBMq,ɗ/C--v p$ JC t4kځ ,aPh"1ÈL*^1MB]Z&B" bZ A` ֳf&e`DBE.$!:\ʴ KbG:IKAFBVZ1H/ 0H384H R*ҠQA!!,pH,Ȥrl:Ca<جv  !9&N#m[C@GaPခ!u eBwXBVEt l !p!n!ZCW C ~BMLcKG_&``@ƌ"IE&LɈp,ec+A!,@pH,Ȥrl:Ï0جv}BɄ2t贚XpcMۑ|/(h||"O#{F p$MhoJn$|LK | KLLgJ# ["wgGMyPLi IJXsH\q:roB&0׽#d$P D)*h0!5h% ,iɄ&'/ļXD+C!,pP*Ȥrl:Шt,%#QzXZjJww\~ B"|qLxS%MLQoNPILtM$bK]OPQnMOK~SݏeIjBQ#SPegC N&^m /8dWLB<$+@l8d0P(c0$F40@¶jNX\2J'7lA!,pH, KȤQX:ШtJrYJ42Gd-tidR2T9 <%&wB&{%F { % F cB5N*CV%+D+d$N$dN%-N-O,dķ%' ε%CF4zܣ%*鑁D CEgmqTPꀬ")4UØ=s>80Ä%L`"ʄ'1|2㔍4R#$[zҠ(5I&>i6!,pH,Hrl:ШtJZH-%N#Q4K* FY%((t L E MzB|%,.--.,\D#NDPC"T%'-'.1E6P6C1PK%//E-P!C2P%U~P7RSQʶ!D'P-O273 pAXYlk`O*FE x)Jr2RJM'B| 5dzJb;Դ $ӠB48'`Q#$ZIT %*P1@!,`!g#Lq$cP8-$g ˌTV%U!LqU%F3w,!,|pH,Hrl:ШtJ [J88J.Q.R((G& L xNC#}U%.'-.,kNqB*P*O%--''E+N2+C 4P$1%# ƕ%//E-P-C,PΫQRUSɛ'!D'C/O SzHH!>>_m LB 2 %¯'$ %3@|@ʃBQ Y'<Pɨ! N=IU[I$!iɣe$T1Pe?) R3 HR;BbjO:EU>.^fphjZR@H c` ! ,hpH,Hd@l:ШtJ&5pRuL.WPY a ~o.}.((ZF uu xDxM| ~,.--.,Cu#CKLg%1'-'.1E6u6dz/,E--C(yS%ʜ'!-/E'~P.ฺB¾h8yuo)SC>X! "PdNJ.^5BELx@RRL AgwVPF NE,jr('H)4,`c"OĔOI@pi0c1ez+"^!,-_pH,Ȥr8ШtJZLL88J.$P z~-}*((G& P wRC#|z%.'-.,DRpZ*T*g%0-''E+R2+C 4T$1%V%/-'!-/E-T-C,TddD'C/SjW$ Q!.ʪ%ȳD2Rr k GIqAJ"PU"A)]r$ 4U Y(L-"sNI" \8@NHI $8 /TFʰ,!,pH,Ȥrl:ШtJN?e4ְxlY* X pP~{%((YF v sDxt,.--.,C x#Cz'%1''1E6z6z,w-y%//Ey-C{(~}-p,F/'D}{|yо1˓[0n$n$ E????C^FC8F*CFBFF4FE|BECEHCHEC@ECECEXDxED`EDpEDEDإED0E DE@DED0EDE}D(E@DFD8FDF DFE$F5E%F@rE,F@E-FEL.FHEl-FE\+FE +FE*FE*FE*FE+FE,FE0F0E4FEX7FEDFFlLF8F|NFFNFFNFFNFT'FMF(0FIFh8FCF<;F@F>F\=FAFx:FTBF9F`BFVFlBFsF|0F~FFtFFnF FjF F |F FlaFElaF`OElaF0OE |FOEjF@DlF`DnFCfFC^FEXFpEBFXEFhElFE8sFHE0sFE$sFE~FEjFEnFEtFEpFEXFF(GFl F@FFxFAF(E9FE4FHEx,FPE#FpE FE FE!F(E$F E&FE'FEp(FE'F؅E%F@EF@EFDE`D E@DED EDED E DEEEEvE5E_E`E4EEpEEEEEE@EE@ExE@ExE@DxEF=F=F@D=F@E$\F@E8oF@ED{FE|FEFFEF EF+E؈F3EF>EpF0MEvFfEDFȁEđFXEFEFEFEVF(EFEtFоEFEVFEVFErFE~FEFXEFEF0FFt F4yFlFnFF@hF!F$`Fh#FXFt#FUF|#FLTFX#FKF FHF FFFxF|FFxFXFFxF FFFEFT FEF F0EF`"FDF#Ft?F1F49FT:Fp1F@F`,FCF|&F8FF( FGFF@GFFTGFF(GF9FE,WFWEAFpWE,F@WE,F D,FCFC FC F D F@WE8EpWEEWE0EE FEF(E FE4FE)FE9FE         cREVGeneral  revUniqueID -'xBa:floppy-cloud-checked.svgP`CSLCD[EoD@>n$n$UUD????CDsF DsFDsFCsFCsFCPrFC4qFRCLpFRCFRC0NECIEC GEC0DECBEC@EC@E-D0@E@FD?EyD?E@D?ED?EDIEDgE@DE@DE@DxEDxE DE DED(EEEEHE;E\FaEFE$ F@EFE\ FE FXE F`E( FEd FE@ FE F@EFEFhEF|F%F FP)FF*FF0+F0Fx+F!F+F`%F*F'F`*F+F )F5Ft&F`=Ft"FCFpFDFFlEFXFpEF`FEFFEF4FEFXAFEFpPF4FHbF,"F tFPFtFtF tFlFXYF`F>FE>F0]E>F]EXYF\E tFpE tFDtFdDtFCDsFFFFFD FXFFFE\FEF@EEEXE@EE EEEEE EEEE,FPELFzEE6EEE8E`D E`D8E DEDEDED@{EDqE`D/EEDKEDpE5DXECXECECEC0,FCzFC~FCTFC0FCdFCFDRFnD4FD(F`EZFp EXF5E$F?EґF0PEJF]EjFlEFEFЛEFFHEFETF(EPF0ENF8E^FErF`EvFPE F0E{FEyFEvF(EtF E|mFEfFE]F FYFFTF0FPFxEOF8EhOF EXOF@EHOF`EOFENFFKFFFFF>F@F`7FF.FF`%FF`"FdFF\FFFIF0EQFExPFEXNFhEPFDFDFDEDEEEEEhEhE@EhEEPiEpEPE EE@EExE@EEyETF_EFJEFJEFJE$F{E$FE4FEAFE4AFETAFpEEFEIF0Ei        cREVGeneral  revUniqueID'xBafloppy-open.svgP`A"wLCDDD@??9C????DDDjDD@DqD@>D@D=DD=D}D=DzD@>D@xD@D6DDCDCD2CD$CD*CqDCD?CzDCDC@DCCC`UC?CCCC*CC$CC2CCCCCDhDDhDCDCDDODD6DD6DCODCODDDrDDDʢD@Dq5DPDD0DDDDDDDDDDDDjDqeDD0D DD`DDDDDtDDqDD nD`DkD DP5iDDhDjDhDDhDDBDD?DDDD@xDrDzD@pD}DoDDoD@DoDqD@pDDrDB        cREVGeneral  revUniqueID 'xBa9floppy-save.svgP`A"QLCDDD@??9C????DDDQD[DQDXDQD UDP5RDRDTDP5PD WDODZDOD]DODDCDCD2CD$CD*CqDCD?CzDCDC@DCCC`UC?CCCC*CC$CC2CCCCCDhDDhDCDCDDODD6DD6DCODCODDtDjDDjDDjDDP5kD0DmDqeD pDDsDDvDDDDDDDDDDЛDq5DDʢDqDDDDDqDʼD@D`DD`D}D`DzDʼD@xDD>ٰjRz cGlx2TimeStamp checksum cREVGeneral  revUniqueID 1236872508693 _DataGridTemplateInstructions_ 444444BJz cGlx2TimeStamp checksum cREVTable  currentview

You can edit the "Row Template" group on this card to customize the look and feel of your data grid form or table. To begin, select the "Row Template" group and choose Object > Edit Group menu item.

 cREVGeneral  revUniqueID 1236872189019 You can edit the "Row Template" group on this card to customize the look and feel of your data grid form or table. To begin, select the "Row Template" group and choose Object > Edit Group menu item. ' Row Templatei7/!Data Grid Templates 1614569882798~ cGlx2TimeStamp checksum cREVGeneral  revUniqueID 1236088553682 BackgroundK@> cGlx2TimeStamp checksum cREVGeneral  revUniqueID 1236188559392 LabelI~ cGlx2TimeStamp checksum cREVGeneral  revUniqueID 1236188559393  'Behavior ScriptEp -- This script defines the behavior of your data grid's custom template. This behavior -- only applies to 'forms' and not 'tables'. on FillInData pDataArray -- This message is sent when the Data Grid needs to populate -- this template with the data from a record. pDataArray is an -- an array containing the records data. -- You do not need to resize any of your template's controls in -- this message. All resizing should be handled in resizeControl. -- Example: set the text of field "Label" of me to pDataArray["label 1"] end FillInData on LayoutControl pControlRect, pWorkingRect local theFieldRect -- This message is sent when you should layout your template's controls. -- This is where you resize the 'Background' graphic, resize fields and -- position objects. -- For fixed height data grid forms you can use items 1 through 4 of pControlRect as -- boundaries for laying out your controls. -- For variable height data grid forms you can use items 1 through 3 of pControlRect as -- boundaries, expanding the height of your control as needed. -- The working rect is defined area of the control you can safelt use. -- Use this rect to make sure your controls don't overlap with any edit mode controls. -- Example: put the rect of field "Label" of me into theFieldRect put item 1 of pWorkingRect into item 1 of theFieldRect put item 3 of pWorkingRect - 5 into item 3 of theFieldRect set the rect of field "Label" of me to theFieldRect set the rect of graphic "Background" of me to pControlRect end LayoutControl on ResetData -- Sent when data is being emptied because the control is no longer being used to display data set the text of field "Label" of me to empty end ResetData on PreFillInData -- Sent right before new data is going to replace existing data in the control end PreFillInData setprop dgHilite pBoolean -- This custom property is set when the highlight of your custom template has -- changed. By default the "Background" graphic will be highlighted for you. -- You only add script here if you want to further customize the highlight. -- Example: if pBoolean then set the foregroundColor of me to the dgProp["hilited text color"] of the dgControl of me else set the foregroundColor of me to empty end if end dgHilite getprop dgDataControl -- Required by library so that it can locate your control. return the long ID of me end dgDataControl on mouseDoubleUp pMouseBtnNum local theKey -- Example of how to edit the contents of a field. -- By passing the index of the record associated with copy of this template being displayed and -- a key (array key) the data grid will automatically save the changes the user -- makes and refresh the UI by calling FillInData and resizeControl. if pMouseBtnNum is 1 then if the dgProps["allow editing"] of the dgControl of me then switch the short name of the target case "Label" put "Label 1" into theKey EditFieldText the long ID of the target, the dgIndex of me, theKey break end switch end if end if pass mouseDoubleUp end mouseDoubleUp  , revDataGridLibrary DG2 Default Action Controlad z  cREVGeneral  revUniqueIDOԽ~wBDG2 Default Action Control%+@;;00dDelete  cREVGeneral  revUniqueID`PԽ~wB +DG2 Default Edit Mode Action Select Controla0 |  cREVGeneral  revUniqueIDQԽ~wB `;;00$ com.livecode.widget.svgpath hilitedflip verticallymaintain aspect ratioanglesvg pathM1216 960L1216 832Q1216 806 1197 787 1178 768 1152 768L384 768Q358 768 339 787 320 806 320 832L320 960Q320 986 339 1005 358 1024 384 1024L1152 1024Q1178 1024 1197 1005 1216 986 1216 960ZM1536 896Q1536 1105 1433 1281.5 1330 1458 1153.5 1561 977 1664 768 1664 559 1664 382.5 1561 206 1458 103 1281.5 0 1105 0 896 0 687 103 510.5 206 334 382.5 231 559 128 768 128 977 128 1153.5 231 1330 334 1433 510.5 1536 687 1536 896ZfillRulenon-zeroiconPresetName minus sign%DG2 Default Edit Mode Reorder Controla$0 ~  cREVGeneral  revUniqueIDQԽ~wB `" com.livecode.widget.svgpath hilitedflip verticallymaintain aspect ratioanglesvg pathM1536 1088L1536 1216Q1536 1242 1517 1261 1498 1280 1472 1280L64 1280Q38 1280 19 1261 0 1242 0 1216L0 1088Q0 1062 19 1043 38 1024 64 1024L1472 1024Q1498 1024 1517 1043 1536 1062 1536 1088ZM1536 576L1536 704Q1536 730 1517 749 1498 768 1472 768L64 768Q38 768 19 749 0 730 0 704L0 576Q0 550 19 531 38 512 64 512L1472 512Q1498 512 1517 531 1536 550 1536 576ZM1536 64L1536 192Q1536 218 1517 237 1498 256 1472 256L64 256Q38 256 19 237 0 218 0 192L0 64Q0 38 19 19 38 0 64 0L1472 0Q1498 0 1517 19 1536 38 1536 64ZfillRulenon-zeroiconPresetNamereorderDG2 Default Left Swipe Controla} x  cREVGeneral  revUniqueIDRԽ~wBK@;;00}8  cREVGeneral  revUniqueID@SԽ~wB `! com.livecode.widget.svgpath hilitedflip verticallymaintain aspect ratioanglesvg pathM512 736L512 1312Q512 1326 503 1335 494 1344 480 1344L416 1344Q402 1344 393 1335 384 1326 384 1312L384 736Q384 722 393 713 402 704 416 704L480 704Q494 704 503 713 512 722 512 736ZM768 736L768 1312Q768 1326 759 1335 750 1344 736 1344L672 1344Q658 1344 649 1335 640 1326 640 1312L640 736Q640 722 649 713 658 704 672 704L736 704Q750 704 759 713 768 722 768 736ZM1024 736L1024 1312Q1024 1326 1015 1335 1006 1344 992 1344L928 1344Q914 1344 905 1335 896 1326 896 1312L896 736Q896 722 905 713 914 704 928 704L992 704Q1006 704 1015 713 1024 722 1024 736ZM1152 1460L1152 512 256 512 256 1460Q256 1482 263 1500.5 270 1519 277.5 1527.5 285 1536 288 1536L1120 1536Q1123 1536 1130.5 1527.5 1138 1519 1145 1500.5 1152 1482 1152 1460ZM480 384L928 384 880 267Q873 258 863 256L546 256Q536 258 529 267ZM1408 416L1408 480Q1408 494 1399 503 1390 512 1376 512L1280 512 1280 1460Q1280 1543 1233 1603.5 1186 1664 1120 1664L288 1664Q222 1664 175 1605.5 128 1547 128 1464L128 512 32 512Q18 512 9 503 0 494 0 480L0 416Q0 402 9 393 18 384 32 384L341 384 411 217Q426 180 465 154 504 128 544 128L864 128Q904 128 943 154 982 180 997 217L1067 384 1376 384Q1390 384 1399 393 1408 402 1408 416ZfillRulenon-zeroiconPresetNametrashDG2 Default Right Swipe Controla} z  cREVGeneral  revUniqueIDSԽ~wBK@;;00}<  cREVGeneral  revUniqueID0TԽ~wB `! com.livecode.widget.svgpath hilitedflip verticallymaintain aspect ratioanglesvg pathM512 736L512 1312Q512 1326 503 1335 494 1344 480 1344L416 1344Q402 1344 393 1335 384 1326 384 1312L384 736Q384 722 393 713 402 704 416 704L480 704Q494 704 503 713 512 722 512 736ZM768 736L768 1312Q768 1326 759 1335 750 1344 736 1344L672 1344Q658 1344 649 1335 640 1326 640 1312L640 736Q640 722 649 713 658 704 672 704L736 704Q750 704 759 713 768 722 768 736ZM1024 736L1024 1312Q1024 1326 1015 1335 1006 1344 992 1344L928 1344Q914 1344 905 1335 896 1326 896 1312L896 736Q896 722 905 713 914 704 928 704L992 704Q1006 704 1015 713 1024 722 1024 736ZM1152 1460L1152 512 256 512 256 1460Q256 1482 263 1500.5 270 1519 277.5 1527.5 285 1536 288 1536L1120 1536Q1123 1536 1130.5 1527.5 1138 1519 1145 1500.5 1152 1482 1152 1460ZM480 384L928 384 880 267Q873 258 863 256L546 256Q536 258 529 267ZM1408 416L1408 480Q1408 494 1399 503 1390 512 1376 512L1280 512 1280 1460Q1280 1543 1233 1603.5 1186 1664 1120 1664L288 1664Q222 1664 175 1605.5 128 1547 128 1464L128 512 32 512Q18 512 9 503 0 494 0 480L0 416Q0 402 9 393 18 384 32 384L341 384 411 217Q426 180 465 154 504 128 544 128L864 128Q904 128 943 154 982 180 997 217L1067 384 1376 384Q1390 384 1399 393 1408 402 1408 416ZfillRulenon-zeroiconPresetNametrashelsoStandardLib P"--|----------------------------------------------------------------- --|----------------------------------------------------------------- --| --| elsoStandardLib 11-19-2020 --| script only stack --| --|----------------------------------------------------------------- constant kMainStack="ODR" --|----------------------------------------------------------------- --| sent when this stack is put into use --|----------------------------------------------------------------- on libraryStack end libraryStack --|----------------------------------------------------------------- --| 11 - 2020 --| call this function instead of iphoneDeviceResolution() --| returns < width,height > of mobile device or --| < width,height > of standAlone stack window --| --| Dependencies: --| isMobile() isStandAlone() isDev() --| constant kMainStack --| the uWorkingDeviceResolution of stack kMainStack --|----------------------------------------------------------------- function elsoDeviceResolution put empty into tWorkingDeviceResolution -- init if isMobile() then put the working screenRect into tWorkingScreenRect put item 3 of tWorkingScreenRect - item 1 of tWorkingScreenRect into tWidth put item 4 of tWorkingScreenRect - item 2 of tWorkingScreenRect into tHeight put tWidth&COMMA&tHeight into tWorkingDeviceResolution return tWorkingDeviceResolution end if if isStandAlone() then put the width of stack kMainStack into tWidth put the height of stack kMainStack into tHeight put tWidth&COMMA&tHeight into tWorkingDeviceResolution return tWorkingDeviceResolution end if if isDev() then -- development put the customPropertySet of stack kMainStack into tOriginalCustomPropSet set the customPropertySet of stack kMainStack to empty -- init put the uWorkingDeviceResolution of stack kMainStack into tWorkingDeviceResolution set the customPropertySet of stack kMainStack to tOriginalCustomPropSet -- tidy if tWorkingDeviceResolution is not empty then return tWorkingDeviceResolution else -- alert developer that this custom prop needs to be set answer "elsoDeviceResolution needs the uWorkingDeviceResolution" &CR& "to be set in the dev() environment as [width , height]" end if end if end elsoDeviceResolution --|----------------------------------------------------------------- --| returns the working "height" of the device --| this is dependent on the orientation and --| is restricted to the fixed resolutions of the app --| Dependencies: --| elsoDeviceResolution() OrientationIs() --| [desktop only]: --| the uWorkingDeviceResolution of stack kMainStack --|----------------------------------------------------------------- function elsoDeviceHeight -- different devices may report the long and short side in different orders -- notably, the iPad2 and the iPad mini -- so we will sort out the correct side put elsoDeviceResolution() into tElsoDeviceResolution put item 1 of tElsoDeviceResolution into tSide1 put item 2 of tElsoDeviceResolution into tSide2 if OrientationIs("landscape") then if tSide1 > tSide2 then put tSide2 into tDeviceHeight else put tSide1 into tDeviceHeight end if else -- portrait if tSide1 > tSide2 then put tSide1 into tDeviceHeight else put tSide2 into tDeviceHeight end if end if return tDeviceHeight end elsoDeviceHeight --|----------------------------------------------------------------- --| returns the working "width" of the device --| this is dependent on the orientation and --| is restricted to the fixed resolutions of the app --| Dependencies: --| elsoDeviceResolution() OrientationIs() --|----------------------------------------------------------------- function elsoDeviceWidth -- different devices may report the long and short side in different orders -- notably, the iPad2 and the iPad mini -- so we will sort out the correct side put elsoDeviceResolution() into tElsoDeviceResolution put item 1 of tElsoDeviceResolution into tSide1 put item 2 of tElsoDeviceResolution into tSide2 if OrientationIs("landscape") then if tSide1 > tSide2 then put tSide1 into tDeviceWidth else put tSide2 into tDeviceWidth end if else -- portrait if tSide1 > tSide2 then put tSide2 into tDeviceWidth else put tSide1 into tDeviceWidth end if end if return tDeviceWidth end elsoDeviceWidth --|----------------------------------------------------------------- --| Returns the aspect ratio as rational number --| by dividing the long edge of the screen (window) by the short edge --| and then rounding to ONE decimal point --| --| uses these custom functions: --| elsoDeviceWidth() elsoDeviceHeight() --|----------------------------------------------------------------- function AspectRatio if elsoDeviceWidth()>elsoDeviceHeight() then return round(elsoDeviceWidth()/elsoDeviceHeight(),1) else return round(elsoDeviceHeight()/elsoDeviceWidth(),1) end if end AspectRatio --|------------------------------------------------------------------------ --| returns the loc of the "usable" part of the window --|------------------------------------------------------------------------ function elsoScreenLoc -- return (round(elsoDeviceWidth()/2)&COMMA&round(((the top of btn "footer")-(the bottom of btn "Header"))/2)+(the bottom of btn "Header")) end elsoScreenLoc --|----------------------------------------------------------------- --| one problem that comes up when determining the correct screen layout --| is when mobileDeviceOrientation() returns orientations like "faceUp" --| or maybe worse, "unknown" --| the solution is to use the screenRect --| --| returns "true" or "false" if pOrientation is not empty --| returns "landscape" or "portrait" if pOrientation is empty --| --| Dev() has dependencies on widget... this code is custom for each app --|----------------------------------------------------------------- function OrientationIs pOrientation if IsMobile() then if pOrientation is empty then if item 3 of screenRect() > item 4 of screenRect() then return "landscape" else return "portrait" end if else -- pOrientation is not empty if item 3 of screenRect() > item 4 of screenRect() then if pOrientation is "landscape" then return "true" end if else if pOrientation is "portrait" then return "true" end if end if return "false" end if -- pOrientation is empty or not else -- not mobile put empty into tOrientation --init if exists(widget "OrientationIs" of cd "LeadCard" of stack kMainStack) then if (the hilite of widget "OrientationIs" of cd "LeadCard" of stack kMainStack) then put "landscape" into tOrientation else put "portrait" into tOrientation end if if pOrientation is empty then return tOrientation else -- pOrientation is not empty if pOrientation is tOrientation then return "true" else return "false" end if end if -- pOrientation is empty or not else -- no orientation widget -- try custom prop put the customPropertySet of stack kMainStack into tOriginalCustomPropSet set the customPropertySet of stack kMainStack to empty -- init put the uOrientation of stack kMainStack into tOrientation set the customPropertySet of stack kMainStack to tOriginalCustomPropSet -- tidy if tOrientation is empty then answer "OrientationIs() needs a widget or customProp for Dev()" else return tOrientation end if -- end if -- widget or not end if -- mobile or not end OrientationIs --|----------------------------------------------------------------- --| Cancel messages in the "pendingmessages" queue --| pWhat may contain "all" OR empty by default --| in which case all messages in the queue are cancelled --| pWhat may contain ID numbers, 1 per line --| or handlerNames, 1 per line --| in which case just those ID or handlerName messages are cancelled --|----------------------------------------------------------------- command CancelPending pWhat if pWhat = "" then put "all" into pWhat put the pendingMessages into pMessages switch pWhat case "all" repeat for each line tLine in pMessages cancel item 1 of tLine end repeat break default repeat for each line tLine in pMessages if tLine contains ("," & pWhat & ",") then cancel item 1 of tLine end if end repeat break end switch end CancelPending --|----------------------------------------------------------------- --| credit: Ken Ray --| Trims all forms of whitespace (CRs, spaces, tabs, etc.) --| from the beginning and end of a text chunk. < Ken Ray > --|----------------------------------------------------------------- function Trim pString get matchText(pString, "(?s)^\s*(.*?)\s*$", tRetVal) return tRetVal end trim --|----------------------------------------------------------------- --| Returns a whole number by trimming everything --| to the right of the decimal point (and the decimal) --|----------------------------------------------------------------- function IntuativeTrunc tNumber if tNumber is not a number then return "error: not a number" set the itemDelimiter to "." return item 1 of tNumber end IntuativeTrunc --|----------------------------------------------------------------- --| open the URL stored with the clicked text --| ShutDown message should occur --| RandoMeister will quit --|----------------------------------------------------------------- on linkClicked pLink if pLink contains "http://" then launch url pLink end if end linkClicked --|-------------------------------------------------- --| Show / Start the iOS ActivityIndicator --| optional use of graphic "ActivityIndicatorBackdrop" --|-------------------------------------------------- command StartActivityIndicator -- pType, pX, pY if IsiOS() then put the screenLoc into tActivityIndicatorLoc -- init if exists(grc "ActivityIndicatorBackdrop" of this cd) then set the loc of grc "ActivityIndicatorBackdrop" of this cd to tActivityIndicatorLoc show grc "ActivityIndicatorBackdrop" of this cd put the loc of grc "ActivityIndicatorBackdrop" of this cd into tActivityIndicatorLoc end if iphoneActivityIndicatorStart "whiteLarge",tActivityIndicatorLoc -- switch pType -- case 1 -- put "grey" into tWheel -- break -- case 2 -- put "white" into tWheel -- break -- default -- put "whiteLarge" into tWheel -- break -- end switch -- if (pX is empty) OR (pY is empty) then -- if exists(grc "ActivityIndicatorBackdrop" of this cd) then -- show grc "ActivityIndicatorBackdrop" of this cd -- end if -- iphoneActivityIndicatorStart tWheel -- else -- if exists(grc "ActivityIndicatorBackdrop" of this cd) then -- set the loc of grc "ActivityIndicatorBackdrop" of this cd to pX, pY -- show grc "ActivityIndicatorBackdrop" of this cd -- end if -- iphoneActivityIndicatorStart tWheel, pX, pY -- end if end if -- iOS if IsAndroid() then if exists(widget "indeterminateSpinner" of this cd) then show widget "indeterminateSpinner" of this cd end if if exists(fld "waitMessage" of this cd) then show fld "waitMessage" end if end if end StartActivityIndicator --|-------------------------------------------------- --| Hide / Close the iOS ActivityIndicator --|-------------------------------------------------- command StopActivityIndicator if IsiOS() then if exists(grc "ActivityIndicatorBackdrop" of this cd) then hide grc "ActivityIndicatorBackdrop" of this cd end if if exists(btn "stopActivity" of this cd) then hide btn "stopActivity" of this cd end if iphoneActivityIndicatorStop end if if IsAndroid() then if exists(fld "waitMessage" of this cd) then hide fld "waitMessage" of this cd end if if exists(widget "indeterminateSpinner" of this cd) then hide widget "indeterminateSpinner" of this cd set the blendlevel of widget "indeterminateSpinner" of this cd to 0 end if end if end StopActivityIndicator --|----------------------------------------------------------------- --| Returns boolean for mobile environment --|----------------------------------------------------------------- function IsMobile return (the environment is "mobile") end IsMobile --|----------------------------------------------------------------- --| Returns boolean for Android --|----------------------------------------------------------------- function IsAndroid return ("android" is in the platform) end IsAndroid --|----------------------------------------------------------------- --| Returns boolean for iOS --|----------------------------------------------------------------- function IsiOS return ("iPhone" is in the platform) end IsiOS --|----------------------------------------------------------------- --| Returns boolean for iOS Simulator (android is a partial work in progress) --|----------------------------------------------------------------- function IsSim return isSimulator() end IsSim function IsSimulator --return (the processor is "i386") return ("Simulator" is in the machine) OR (IsBeta() AND ("Android SDK built for X86" is in the machine)) end IsSimulator --|----------------------------------------------------------------- --| Returns boolean for development environment --|----------------------------------------------------------------- function IsDev return (the environment is "development") end IsDev --|----------------------------------------------------------------- --| Returns boolean for Beta environment --| < a private beta property > --|----------------------------------------------------------------- function IsBeta return (the uBetaVersion of stack kMainstack) end IsBeta function IsDevOrBeta return (IsDev() OR IsBeta()) end IsDevOrBeta --|----------------------------------------------------------------- --| returns boolean for iPad --| --> mergDeviceName() --|----------------------------------------------------------------- function IsiPad return "iPad" is in the machine -- return (DeviceType("tablet") AND the platform is "iphone") end IsiPad --|----------------------------------------------------------------- --| Returns boolean for iPhoneX model based on aspect ratio --| Dependencies: --| elsoDeviceWidth() elsoDeviceHeight() --|----------------------------------------------------------------- function IsiPhoneX -- original iPhones had an aspect ratio of about 1.5 -- iPhone 4 and later have an aspect ratio of about 1.8 -- iPhoneX models have an aspect ratio of about 2.16 --> 2.2 -- -- figure out if it is an iPhoneX model -- --> iPhoneX & XS = 375x812 (@3x) 1125x2436 -- --> iPhoneXR = 414x896 (@2x) 828x1792 -- --> iPhoneXSMax = 414x896 (@3x) 1242x2688 -- -- This is a hack that relies on knowing exact dimensions for every model -- put 375&COMMA&812&CR&812&COMMA&375&CR&414&COMMA&896&CR&896&COMMA&414 into tXRes -- return (IsiOS() AND deviceType("phone") AND elsoDeviceResolution() is among the lines of tXRes) if not ("iPhone" is in the machine) then return "false" end if -- now we know it is an iPhone, just not whether it has a notch and home indicator -- make the final determination based on aspect ratio put elsoDeviceWidth()&COMMA&elsoDeviceHeight() into tDeviceResolution -- the short edge of the dimension needs to be the first number if (item 1 of tDeviceResolution)>(item 2 of tDeviceResolution) then put item 2 of tDeviceResolution&COMMA&item 1 of tDeviceResolution into tDeviceResolution end if -- determine the aspect ratio return (round(((item 2 of tDeviceResolution) / (item 1 of tDeviceResolution)),1) >=2.2) end IsiPhoneX --|--------------- HaCk ALeRT ------------------------------------ --|----------------------------------------------------------------- --| returns boolean for iOS iPhones with a "Home Indicator" --| (rather than a physical "Home" button) --| we are using a NON-GENERALIZED "hack" for determining if an iPad uses a "Home Indicator" --| --> mergDeviceName() requires knowing all the relevant device names ahead of time --| Dependencies: --| IsiPhoneX() IsiPad() mergDeviceName() --|----------------------------------------------------------------- function HasHomeIndicator if IsiPhoneX() OR (IsiPad() AND mergDeviceName() is among the items of "iPad Pro (12.9-inch) (3rd generation),iPad Pro (12.9-inch) (4th generation)") then return "true" else return "false" end if end HasHomeIndicator --|------------------------------------------------------------------------ --| returns the horizontal pad (away from the notch) --| of an iPhoneX --| pSide is an optional param to specicfy padding away from a specific side --| Dependencies: --| IsiPhoneX() OrientationIs() --|------------------------------------------------------------------------ function iPhoneXPad pSide if pSide is not among the items "left,right" then put empty into pSide end if if IsiPhoneX() AND OrientationIs("landscape") then if pSide is empty then return 25 -- the horizontal offset used when an iPhoneX is in landscape end if if pSide is word 2 of mobileDeviceOrientation() then return 25 else -- wrong side return 0 end if else -- not an iPhone OR not landscape return 0 end if end iPhoneXPad --|----------------------------------------------------------------- --| Returns boolean for stand alone application --| returns true for non Dev() Desktop application --|----------------------------------------------------------------- function IsStandAlone return ("standalone" is in the environment) end IsStandAlone --|----------------------------------------------------------------- --| Returns boolean for Microsoft Windows environment --|----------------------------------------------------------------- function IsWindows return ("win" is in the platform) end IsWindows function IsWin return IsWindows() end IsWin --|----------------------------------------------------------------- --| Returns boolean for Mac OSX environment --|----------------------------------------------------------------- function IsOSX -- Mac OS system versions are of the form "x.y.z" -- OS X system versions are "10.x.x" set the itemdelimiter to "." if (the platform is "MacOS") AND ((item 1 of the systemversion)>=10) then return "true" else return "false" end if end IsOSX --|----------------------------------------------------------------- --| Returns boolean for Linux environment --|----------------------------------------------------------------- function IsLinux return ("linux" is in the platform) end IsLinux --|------------------------------------------------------------------------ --| a slightly more explainatory way to call DeviceType() --|------------------------------------------------------------------------ function DeviceTypeIs pType return DeviceType(pType) end DeviceTypeIs --|----------------------------------------------------------------- --| September 2020 -- fixed a bug in the Android detection --| July 2020 -- added "desktop" as a param type --|----------------------------------------------------------------- --| Returns "tablet" or "phone" or "desktop" if pType is empty --| Returns "true" or "false" if pType is "tablet" or "phone" or "desktop" --| --| in the development environment it will report "phone" or "tablet" based on aspect ratio --| --| Returns correctly in all environments --| makes the determination based on aspect ratio --| iPhone with home indicator..... 2.16 --| iPhone with home button........ 1.78 --| android tablets as narrow as.. (1.70) <-- some android "tablets" (ex: Nexus 10) will return as "phone" --|<-----------we break here------------> 1.60 --| iPad 11 & 10.5................. 1.43 --| iPad 12.9...................... 1.33 --| iPad mini..................... 1.31 --| --| uses these custom functions: --| elsoDeviceWidth() elsoDeviceHeight() --|----------------------------------------------------------------- function DeviceType pType set the itemDel to COMMA if pType is not among the items "phone,tablet,desktop" then put empty into pType end if if IsDev() then -- if exists(widget "DeviceType" of cd 1 of stack kMainStack) then if exists(widget "DeviceType" of cd 1 of stack "developerTool") then if the hilite of widget "DeviceType" of cd 1 of stack "developerTool" then put "tablet" into tWidgetSetting else put "phone" into tWidgetSetting end if if pType is empty then -- return a device type return tWidgetSetting else -- return boolean return (pType is tWidgetSetting) end if else -- no widget to change device type in Dev() answer "DeviceType() requires a widget in Dev()" end if end if if IsStandAlone() then if pType is empty then return "desktop" else return (pType is "desktop") end if end if -- certain solution for iOS if "iPhone" is in the machine then if pType is empty then return "phone" else return (pType is "phone") -- boolean end if end if if "iPad" is in the machine then if pType is empty then return "tablet" else return (pType is "tablet") -- boolean end if end if -- solve for Android (somewhat murky in the phablet category) -- another solution: calculate the physical diagonal screen size -- call anything over 7.5 diagonal inches a tablet -- sadly, we have no way of knowing what the real-world ppi is (size of pixels) -- but we can still make use of the total number of diagonal pixels to help clarify -- width squared + height squared = diagonal squared put sqrt((elsoDeviceWidth()*elsoDeviceWidth())+(elsoDeviceHeight()*elsoDeviceHeight())) into tDiagonalPixels ------- ORIGINAL SOLUTION... fails on narrow tablets put elsoDeviceWidth()&COMMA&elsoDeviceHeight() into tDeviceResolution -- the short edge of the dimension needs to be the first number if (item 1 of tDeviceResolution)>(item 2 of tDeviceResolution) then put item 2 of tDeviceResolution&COMMA&item 1 of tDeviceResolution into tDeviceResolution end if -- even though it might have a "tablet" aspect ratio -- don't call it a tablet if it is "tiny" if (item 1 of tDeviceResolution) < 325 then if pType is empty then return "phone" else return (pType is "phone") end if end if -- 640x1136 = 1.8 (16:9 aspect ratio) 320x480 = 1.5 put round(((item 2 of tDeviceResolution) / (item 1 of tDeviceResolution)),2) into tAspectRatio -- this will catch many phones, including really big ones if tAspectRatio > 1.79 then if pType is empty then return "phone" else return (pType is "phone") end if end if -- with large phones already filtered out -- this should catch tablets -- iPad aspect ratio -- ex: 768x1024 1536x2048 1.333 aspect for iPad -- iPad mini diagonal of 1280 aspect ratio of 1.33 -- Galaxy Tab 4.7.1 diagonal of 1406 aspect ratio of 1.68 if tDiagonalPixels >1270 OR tAspectRatio < 1.45 then -- tablets if pType is empty then return "tablet" else return (pType is "tablet") end if else -- phone if pType is empty then return "phone" else return (pType is "phone") end if end if -- -- iPad mini has a diagonal of 1280 iPhone XS Max has a diagonal of 1480 -- if (round(((item 2 of tDeviceResolution) / (item 1 of tDeviceResolution)),2) >1.45) OR (tDiagonalPixels < 1270) then -- -- iPhone 5 and above -- if pType is empty then -- return "phone" -- else -- return (pType is "phone") -- end if -- else -- less than or equal to 1.45 -- -- iPad aspect ratio -- ex: 768x1024 1536x2048 1.333 aspect for iPad -- if pType is empty then -- return "tablet" -- else -- return (pType is "tablet") -- end if -- end if end DeviceType --|----------------------------------------------------------------- --| Returns an empirically derived estimated location for the top edge of the keyboard --| that includes a small (.2) amount of "grace"... so this will not butt edges --| --| using --| SHOULD allow us to discover the exact top of the keyboard --| --> return item 4 of the effective working screenRect --| the keyboard would need to visible, however --| using this function allows positioning animation ahead of the keyboard appearance --| landscape keyboard appears to cover exactly half of the iPhone / iPod touch --| --> iPhone original Portrait is 264 --| --> iPhone original Landscape is 160 --| --> iPhone(3.5in) Retina Portrait is 744 --| --> iPhone(3.5in) Retina Landscape is 320 --| --> iPhone(4in) Retina Portrait is 920 --| --> iPhone(4in) Retina Landscape is 320 --| --> iPad original Portrait is 750 --| --> iPad original Landscape is 390 --| --> iPad Retina Portrait is 1500 --| --> iPad Retina Landscape is 780 --|----------------------------------------------------------------- function KeyBoardTop put elsoDeviceHeight() into tHeight if OrientationIs("Landscape") then return (tHeight - round(tHeight/1.8)) else return (tHeight - round(tHeight/2.2)) end if end KeyBoardTop c;   cREVGeneral breakpointstates breakpointsbreakpointconditionsscripteditorselection15056 scalefactor?scripteditorvscroll!M @:about P on mouseUp close me end mouseUp -- trap these on preopenStack put "version"&& fld "versionNumber" of stack "Calendar Events" into fld "version" of stack "about" end preopenStack on openStack -- trap end openStack on closeStack -- trap end closeStack C)?  U(Message),unicodeU(Message),unicode U (Message) U(Message),unicode U(default),unicode cREVGeometryCache stackID cREVGeneral breakpointstates breakpointsbreakpointconditionsscripteditorselection50 scalefactor?scripteditorvscroll @: cREVGeometryCacheIDs  1622507711504 1622507785832 1618648516627 1622507785833cREVGeometrycache totalorder ButtonbB -- on mouseUp -- launch url "/livecode/calendarevents.html" -- GoURL -- pass mouseUp -- end mouseUp @RQy-)  cREVGeneral  revUniqueID0wBscripteditorvscrollscripteditorselection108  91ֱFld@jBBy  cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueIDPwBtablefalse  Calendar Events % © 2021 91ֱ %4 version P n y  cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueIDPwBtablefalse  version 1.2.7 '  MITLicensePk\ {  cRevTable  cellFormatfalsemaxColumnCountcellEditfalse cREVGeneral  revUniqueIDOwBtablefalse  MIT License $